@ecopages/core 0.2.0-beta.2 → 0.2.0-beta.20

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 (273) hide show
  1. package/README.md +1 -1
  2. package/package.json +163 -3
  3. package/src/adapters/abstract/application-adapter.d.ts +104 -5
  4. package/src/adapters/abstract/application-adapter.js +125 -1
  5. package/src/adapters/abstract/server-adapter.d.ts +6 -2
  6. package/src/adapters/abstract/ws-pattern-matcher.d.ts +60 -0
  7. package/src/adapters/abstract/ws-pattern-matcher.js +61 -0
  8. package/src/adapters/bun/client-bridge.d.ts +1 -0
  9. package/src/adapters/bun/client-bridge.js +3 -0
  10. package/src/adapters/bun/create-app.d.ts +6 -4
  11. package/src/adapters/bun/create-app.js +65 -43
  12. package/src/adapters/bun/hmr-manager.d.ts +2 -1
  13. package/src/adapters/bun/hmr-manager.js +2 -2
  14. package/src/adapters/bun/server-adapter.d.ts +90 -22
  15. package/src/adapters/bun/server-adapter.js +265 -84
  16. package/src/adapters/bun/static-preview-host.js +25 -20
  17. package/src/adapters/create-app.d.ts +13 -11
  18. package/src/adapters/create-app.js +2 -51
  19. package/src/adapters/index.d.ts +1 -1
  20. package/src/adapters/index.js +1 -2
  21. package/src/adapters/node/create-app.d.ts +5 -1
  22. package/src/adapters/node/create-app.js +51 -12
  23. package/src/adapters/node/node-client-bridge.d.ts +1 -0
  24. package/src/adapters/node/node-client-bridge.js +3 -0
  25. package/src/adapters/node/node-hmr-manager.d.ts +2 -1
  26. package/src/adapters/node/node-hmr-manager.js +2 -2
  27. package/src/adapters/node/server-adapter-dependencies.js +4 -0
  28. package/src/adapters/node/server-adapter.d.ts +62 -20
  29. package/src/adapters/node/server-adapter.js +161 -131
  30. package/src/adapters/node/static-content-server.d.ts +1 -0
  31. package/src/adapters/node/static-content-server.js +10 -2
  32. package/src/adapters/node/static-preview-host.js +27 -12
  33. package/src/adapters/shared/bun-user-websocket-lifecycle.d.ts +22 -0
  34. package/src/adapters/shared/bun-user-websocket-lifecycle.js +96 -0
  35. package/src/adapters/shared/copy-runtime-public-dir.d.ts +6 -0
  36. package/src/adapters/shared/copy-runtime-public-dir.js +24 -0
  37. package/src/adapters/shared/fs-server-response-matcher.d.ts +2 -0
  38. package/src/adapters/shared/fs-server-response-matcher.js +40 -18
  39. package/src/adapters/shared/hmr-entrypoint-registrar.d.ts +0 -2
  40. package/src/adapters/shared/hmr-entrypoint-registrar.js +3 -26
  41. package/src/adapters/shared/hmr-html-response.d.ts +1 -1
  42. package/src/adapters/shared/hmr-html-response.js +4 -1
  43. package/src/adapters/shared/node-http-websocket-upgrades.d.ts +21 -0
  44. package/src/adapters/shared/node-http-websocket-upgrades.js +100 -0
  45. package/src/adapters/shared/port-manager.d.ts +116 -0
  46. package/src/adapters/shared/port-manager.js +196 -0
  47. package/src/adapters/shared/runtime-app-bootstrap.d.ts +2 -1
  48. package/src/adapters/shared/runtime-app-bootstrap.js +3 -6
  49. package/src/adapters/shared/runtime-server-lifecycle.d.ts +38 -0
  50. package/src/adapters/shared/runtime-server-lifecycle.js +43 -0
  51. package/src/adapters/shared/server-adapter.d.ts +2 -1
  52. package/src/adapters/shared/server-adapter.js +13 -6
  53. package/src/adapters/shared/server-route-handler.d.ts +4 -1
  54. package/src/adapters/shared/server-route-handler.js +5 -2
  55. package/src/adapters/shared/server-static-builder.d.ts +23 -32
  56. package/src/adapters/shared/server-static-builder.js +97 -73
  57. package/src/adapters/shared/shared-hmr-manager.d.ts +28 -0
  58. package/src/adapters/shared/shared-hmr-manager.js +119 -9
  59. package/src/adapters/shared/static-preview-host.d.ts +5 -0
  60. package/src/adapters/shared/websocket-lifecycle.d.ts +24 -0
  61. package/src/adapters/shared/websocket-lifecycle.js +55 -0
  62. package/src/build/README.md +77 -14
  63. package/src/build/app-build-manifest-runtime.d.ts +10 -0
  64. package/src/build/app-build-manifest-runtime.js +90 -0
  65. package/src/build/build-adapter.d.ts +17 -319
  66. package/src/build/build-adapter.js +28 -94
  67. package/src/build/build-contracts.d.ts +97 -0
  68. package/src/build/build-contracts.js +0 -0
  69. package/src/build/build-input-fingerprint.d.ts +28 -0
  70. package/src/build/build-input-fingerprint.js +31 -0
  71. package/src/build/build-profile-options.d.ts +7 -0
  72. package/src/build/build-profile-options.js +37 -0
  73. package/src/build/build-runtime.d.ts +24 -0
  74. package/src/build/build-runtime.js +65 -0
  75. package/src/build/deduping-build-executor.d.ts +28 -0
  76. package/src/build/deduping-build-executor.js +56 -0
  77. package/src/build/jsx-ownership-plugins.d.ts +24 -0
  78. package/src/build/jsx-ownership-plugins.js +41 -0
  79. package/src/build/pages-unified-graph-build.d.ts +31 -0
  80. package/src/build/pages-unified-graph-build.js +206 -0
  81. package/src/build/parallel-build-executor.d.ts +21 -0
  82. package/src/build/parallel-build-executor.js +52 -0
  83. package/src/build/production-build-cache.d.ts +20 -0
  84. package/src/build/production-build-cache.js +63 -0
  85. package/src/build/rolldown-adapter-helpers.d.ts +3 -6
  86. package/src/build/rolldown-adapter-helpers.js +71 -6
  87. package/src/build/rolldown-build-adapter.d.ts +2 -6
  88. package/src/build/rolldown-build-adapter.js +3 -3
  89. package/src/build/rolldown-build-invocation-metrics.d.ts +12 -0
  90. package/src/build/rolldown-build-invocation-metrics.js +43 -0
  91. package/src/build/rolldown-plugin-bridge.d.ts +15 -1
  92. package/src/build/rolldown-plugin-bridge.js +20 -5
  93. package/src/build/rolldown-source-transform-pass.d.ts +15 -0
  94. package/src/build/rolldown-source-transform-pass.js +58 -0
  95. package/src/build/runtime-build-executor.d.ts +8 -15
  96. package/src/build/runtime-build-executor.js +12 -15
  97. package/src/build/runtime-build-output-normalizer.d.ts +7 -0
  98. package/src/build/runtime-build-output-normalizer.js +94 -2
  99. package/src/build/server-entry-build-cache.d.ts +44 -0
  100. package/src/build/server-entry-build-cache.js +161 -0
  101. package/src/client/navigation-scripts/classification.d.ts +16 -0
  102. package/src/client/navigation-scripts/classification.js +41 -0
  103. package/src/client/navigation-scripts/index.d.ts +3 -0
  104. package/src/client/navigation-scripts/index.js +23 -0
  105. package/src/client/navigation-scripts/navigation-scripts.test.browser.js +65 -0
  106. package/src/client/navigation-scripts/registry.d.ts +11 -0
  107. package/src/client/navigation-scripts/registry.js +12 -0
  108. package/src/client/navigation-scripts/rerun-queue.d.ts +37 -0
  109. package/src/client/navigation-scripts/rerun-queue.js +73 -0
  110. package/src/client/scroll.d.ts +17 -0
  111. package/src/client/scroll.js +25 -0
  112. package/src/client/scroll.test.browser.js +37 -0
  113. package/src/client/view-transitions.d.ts +16 -0
  114. package/src/client/view-transitions.js +63 -0
  115. package/src/config/config-builder.d.ts +12 -7
  116. package/src/config/config-builder.js +12 -9
  117. package/src/dev/client-bridge-registry.d.ts +5 -0
  118. package/src/dev/client-bridge-registry.js +15 -0
  119. package/src/dev/dev-client-ownership.d.ts +7 -0
  120. package/src/dev/dev-client-ownership.js +6 -0
  121. package/src/dev/hmr-manager-registry.d.ts +8 -0
  122. package/src/dev/hmr-manager-registry.js +15 -0
  123. package/src/dev/host-runtime.d.ts +3 -0
  124. package/src/dev/host-runtime.js +7 -0
  125. package/src/dev/runtime-server-started-message.d.ts +2 -0
  126. package/src/dev/runtime-server-started-message.js +6 -0
  127. package/src/eco/README.md +11 -0
  128. package/src/eco/eco-declared-component.d.ts +14 -0
  129. package/src/eco/eco-declared-component.js +16 -0
  130. package/src/eco/eco.browser.js +5 -6
  131. package/src/eco/eco.js +5 -6
  132. package/src/eco/eco.types.d.ts +2 -2
  133. package/src/eco/page-layout-normalization.d.ts +21 -0
  134. package/src/eco/page-layout-normalization.js +59 -0
  135. package/src/errors/index.d.ts +1 -0
  136. package/src/errors/index.js +7 -1
  137. package/src/errors/undeclared-component-dependency-error.d.ts +11 -0
  138. package/src/errors/undeclared-component-dependency-error.js +17 -0
  139. package/src/hmr/client/hmr-runtime.js +5 -2
  140. package/src/hmr/hmr-entrypoint-output.d.ts +28 -0
  141. package/src/hmr/hmr-entrypoint-output.js +58 -0
  142. package/src/hmr/strategies/js-hmr-strategy.d.ts +3 -0
  143. package/src/hmr/strategies/js-hmr-strategy.js +68 -18
  144. package/src/hmr/strategies/server-rendered-template-hmr-strategy.d.ts +17 -0
  145. package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +29 -0
  146. package/src/integrations/ghtml/ghtml-renderer.d.ts +2 -6
  147. package/src/integrations/ghtml/ghtml-renderer.js +2 -51
  148. package/src/integrations/ghtml/ghtml.plugin.d.ts +3 -16
  149. package/src/integrations/ghtml/ghtml.plugin.js +7 -14
  150. package/src/plugins/define-integration.d.ts +26 -0
  151. package/src/plugins/define-integration.js +19 -0
  152. package/src/plugins/eco-component-meta-plugin.js +5 -8
  153. package/src/plugins/foreign-jsx-override-plugin.js +4 -6
  154. package/src/plugins/integration-plugin.d.ts +19 -9
  155. package/src/plugins/integration-plugin.js +7 -4
  156. package/src/plugins/jsx-import-source.utils.d.ts +8 -0
  157. package/src/plugins/jsx-import-source.utils.js +26 -0
  158. package/src/plugins/processor.d.ts +19 -4
  159. package/src/plugins/processor.js +22 -4
  160. package/src/plugins/source-transform.d.ts +31 -0
  161. package/src/plugins/source-transform.js +23 -1
  162. package/src/route-renderer/GRAPH.md +8 -8
  163. package/src/route-renderer/README.md +30 -28
  164. package/src/route-renderer/orchestration/component-graph-collectors.d.ts +10 -0
  165. package/src/route-renderer/orchestration/component-graph-collectors.js +143 -0
  166. package/src/route-renderer/orchestration/component-graph.d.ts +36 -0
  167. package/src/route-renderer/orchestration/component-graph.js +98 -0
  168. package/src/route-renderer/orchestration/component-render-context.d.ts +3 -10
  169. package/src/route-renderer/orchestration/component-render-context.js +3 -1
  170. package/src/route-renderer/orchestration/declared-ownership-graph.d.ts +1 -18
  171. package/src/route-renderer/orchestration/declared-ownership-graph.js +5 -31
  172. package/src/route-renderer/orchestration/document-shell-render.service.d.ts +90 -0
  173. package/src/route-renderer/orchestration/document-shell-render.service.js +120 -0
  174. package/src/route-renderer/orchestration/foreign-subtree-execution.service.d.ts +59 -29
  175. package/src/route-renderer/orchestration/foreign-subtree-execution.service.js +164 -35
  176. package/src/route-renderer/orchestration/global-injector-assets.service.d.ts +7 -0
  177. package/src/route-renderer/orchestration/global-injector-assets.service.js +32 -0
  178. package/src/route-renderer/orchestration/integration-renderer.d.ts +18 -54
  179. package/src/route-renderer/orchestration/integration-renderer.js +113 -195
  180. package/src/route-renderer/orchestration/integration-route-render-adapter.d.ts +38 -0
  181. package/src/route-renderer/orchestration/integration-route-render-adapter.js +27 -0
  182. package/src/route-renderer/orchestration/layout-shell-props.service.d.ts +20 -0
  183. package/src/route-renderer/orchestration/layout-shell-props.service.js +31 -0
  184. package/src/route-renderer/orchestration/ownership-validation.service.d.ts +5 -0
  185. package/src/route-renderer/orchestration/ownership-validation.service.js +29 -1
  186. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.d.ts +9 -0
  187. package/src/route-renderer/orchestration/page-browser-graph-contribution.loader.js +7 -0
  188. package/src/route-renderer/orchestration/page-browser-graph.service.d.ts +25 -0
  189. package/src/route-renderer/orchestration/page-browser-graph.service.js +174 -0
  190. package/src/route-renderer/orchestration/render-output.utils.d.ts +2 -0
  191. package/src/route-renderer/orchestration/render-output.utils.js +4 -0
  192. package/src/route-renderer/orchestration/route-html-finalization.service.d.ts +17 -0
  193. package/src/route-renderer/orchestration/route-html-finalization.service.js +26 -0
  194. package/src/route-renderer/orchestration/route-prepared-options.builder.d.ts +26 -0
  195. package/src/route-renderer/orchestration/route-prepared-options.builder.js +54 -0
  196. package/src/route-renderer/orchestration/route-prepared-options.utils.d.ts +4 -0
  197. package/src/route-renderer/orchestration/route-prepared-options.utils.js +33 -0
  198. package/src/route-renderer/orchestration/route-render-orchestrator.d.ts +8 -39
  199. package/src/route-renderer/orchestration/route-render-orchestrator.js +53 -461
  200. package/src/route-renderer/orchestration/string-markup-renderer.d.ts +14 -0
  201. package/src/route-renderer/orchestration/string-markup-renderer.js +55 -0
  202. package/src/route-renderer/page-loading/component-dependency-collection.js +5 -2
  203. package/src/router/client/link-intent.d.ts +11 -21
  204. package/src/router/client/link-intent.js +10 -0
  205. package/src/router/client/link-intent.test.browser.js +12 -1
  206. package/src/router/client/link-navigation-policy.d.ts +45 -0
  207. package/src/router/client/link-navigation-policy.js +101 -0
  208. package/src/router/client/link-navigation-policy.test.browser.d.ts +1 -0
  209. package/src/router/client/link-navigation-policy.test.browser.js +92 -0
  210. package/src/router/client/navigation-coordinator.d.ts +1 -1
  211. package/src/router/client/navigation-coordinator.js +12 -2
  212. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.d.ts +1 -0
  213. package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +1 -0
  214. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.d.ts +4 -2
  215. package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +27 -7
  216. package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +36 -14
  217. package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +17 -3
  218. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.d.ts +0 -3
  219. package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +2 -31
  220. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.d.ts +0 -3
  221. package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +2 -30
  222. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.d.ts +2 -0
  223. package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +43 -0
  224. package/src/services/assets/browser-bundle.service.d.ts +3 -1
  225. package/src/services/assets/browser-bundle.service.js +17 -5
  226. package/src/services/html/html-transformer.service.d.ts +1 -1
  227. package/src/services/html/html-transformer.service.js +4 -4
  228. package/src/services/invalidation/development-invalidation.service.d.ts +20 -2
  229. package/src/services/invalidation/development-invalidation.service.js +56 -2
  230. package/src/services/module-loading/README.md +49 -0
  231. package/src/services/module-loading/app-server-module-transpiler.service.js +5 -17
  232. package/src/services/module-loading/page-module-import.service.d.ts +5 -1
  233. package/src/services/module-loading/page-module-import.service.js +97 -45
  234. package/src/services/module-loading/route-module-build-cache-registry.d.ts +24 -0
  235. package/src/services/module-loading/route-module-build-cache-registry.js +37 -0
  236. package/src/services/module-loading/route-module-build-cache.d.ts +4 -0
  237. package/src/services/module-loading/route-module-build-cache.js +38 -0
  238. package/src/services/module-loading/route-module-build-cache.store.d.ts +57 -0
  239. package/src/services/module-loading/route-module-build-cache.store.js +207 -0
  240. package/src/services/module-loading/route-module-build-manifest.d.ts +61 -0
  241. package/src/services/module-loading/route-module-build-manifest.js +116 -0
  242. package/src/services/module-loading/route-module-dependency-hasher.d.ts +81 -0
  243. package/src/services/module-loading/route-module-dependency-hasher.js +121 -0
  244. package/src/services/module-loading/server-module-transpiler.service.js +1 -1
  245. package/src/static-site-generator/README.md +33 -0
  246. package/src/static-site-generator/static-build-invalidation.d.ts +10 -0
  247. package/src/static-site-generator/static-build-invalidation.js +59 -0
  248. package/src/static-site-generator/static-export-context.d.ts +22 -0
  249. package/src/static-site-generator/static-export-context.js +0 -0
  250. package/src/static-site-generator/static-site-generator.d.ts +17 -20
  251. package/src/static-site-generator/static-site-generator.js +188 -85
  252. package/src/types/internal-types.d.ts +16 -9
  253. package/src/types/public-types.d.ts +138 -26
  254. package/src/utils/parse-cli-args.d.ts +2 -0
  255. package/src/utils/parse-cli-args.js +6 -0
  256. package/src/utils/server-utils.module.d.ts +3 -2
  257. package/src/utils/server-utils.module.js +5 -6
  258. package/src/utils/static-file-extensions.d.ts +12 -0
  259. package/src/utils/static-file-extensions.js +48 -0
  260. package/src/watchers/project-watcher-ignore.d.ts +11 -0
  261. package/src/watchers/project-watcher-ignore.js +16 -0
  262. package/src/watchers/project-watcher.d.ts +25 -2
  263. package/src/watchers/project-watcher.js +130 -23
  264. package/src/build/rolldown-dev-build-adapter.d.ts +0 -82
  265. package/src/build/rolldown-dev-build-adapter.js +0 -166
  266. package/src/hmr/hmr.postcss.test.e2e.js +0 -31
  267. package/src/hmr/hmr.test.e2e.js +0 -43
  268. package/src/route-renderer/orchestration/ownership-planning.service.d.ts +0 -24
  269. package/src/route-renderer/orchestration/ownership-planning.service.js +0 -63
  270. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.d.ts +0 -91
  271. package/src/route-renderer/orchestration/queued-foreign-subtree-resolution.service.js +0 -170
  272. /package/src/{hmr/hmr.postcss.test.e2e.d.ts → client/navigation-scripts/navigation-scripts.test.browser.d.ts} +0 -0
  273. /package/src/{hmr/hmr.test.e2e.d.ts → client/scroll.test.browser.d.ts} +0 -0
@@ -15,8 +15,8 @@
15
15
  * Timestamped navigation intent captured from pointer or hover activity.
16
16
  */
17
17
  export type EcoPendingNavigationIntent = {
18
- href: string;
19
- timestamp: number;
18
+ href: string;
19
+ timestamp: number;
20
20
  };
21
21
  /**
22
22
  * Finds the nearest matching anchor within an event's composed path.
@@ -28,26 +28,16 @@ export type EcoPendingNavigationIntent = {
28
28
  * @param linkSelector - Selector that identifies navigable anchors.
29
29
  * @returns The matched anchor element, or `null` when no matching anchor exists.
30
30
  */
31
- export declare function getAnchorFromNavigationEvent(
32
- event: MouseEvent | PointerEvent,
33
- linkSelector: string,
34
- ): HTMLAnchorElement | null;
31
+ export declare function getAnchorFromNavigationEvent(event: MouseEvent | PointerEvent, linkSelector: string): HTMLAnchorElement | null;
32
+ /**
33
+ * Returns whether an href targets a static asset that should use a full document load.
34
+ */
35
+ export declare function isStaticAssetHref(href: string): boolean;
35
36
  /**
36
37
  * Resolves a previously captured intent while a navigation is still in flight.
37
38
  *
38
- * Pending intents expire quickly because they are only meant to bridge the gap
39
- * between pointer or hover capture and the later click event when the DOM or
40
- * active runtime changes during a rapid navigation sequence.
41
- *
42
- * @param intent - Previously captured pointer or hover intent.
43
- * @param hasInFlightNavigation - Whether a router navigation is still active.
44
- * @param now - Current monotonic timestamp, usually from `performance.now()`.
45
- * @param maxAgeMs - Maximum allowed age for the recovered intent.
46
- * @returns The intended href when still valid, otherwise `null`.
39
+ * @remarks
40
+ * Pending intents expire quickly because they bridge pointer or hover capture
41
+ * and the later click when the DOM changes during a rapid navigation sequence.
47
42
  */
48
- export declare function recoverPendingNavigationHref(
49
- intent: EcoPendingNavigationIntent | null,
50
- hasInFlightNavigation: boolean,
51
- now: number,
52
- maxAgeMs?: number,
53
- ): string | null;
43
+ export declare function recoverPendingNavigationHref(intent: EcoPendingNavigationIntent | null, hasInFlightNavigation: boolean, now: number, maxAgeMs?: number): string | null;
@@ -1,3 +1,4 @@
1
+ import { hasKnownStaticExtension } from "../../utils/static-file-extensions.js";
1
2
  function getAnchorFromNavigationEvent(event, linkSelector) {
2
3
  const eventTarget = event.target;
3
4
  if (eventTarget instanceof HTMLAnchorElement && eventTarget.matches(linkSelector)) {
@@ -19,6 +20,14 @@ function getAnchorFromNavigationEvent(event, linkSelector) {
19
20
  (target) => target instanceof HTMLAnchorElement && target.matches(linkSelector)
20
21
  );
21
22
  }
23
+ function isStaticAssetHref(href) {
24
+ try {
25
+ const url = new URL(href, "http://localhost");
26
+ return hasKnownStaticExtension(url.pathname);
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
22
31
  function recoverPendingNavigationHref(intent, hasInFlightNavigation, now, maxAgeMs = 1e3) {
23
32
  if (!intent || !hasInFlightNavigation) {
24
33
  return null;
@@ -30,5 +39,6 @@ function recoverPendingNavigationHref(intent, hasInFlightNavigation, now, maxAge
30
39
  }
31
40
  export {
32
41
  getAnchorFromNavigationEvent,
42
+ isStaticAssetHref,
33
43
  recoverPendingNavigationHref
34
44
  };
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from "vitest";
2
- import { getAnchorFromNavigationEvent, recoverPendingNavigationHref } from "./link-intent.js";
2
+ import { getAnchorFromNavigationEvent, isStaticAssetHref, recoverPendingNavigationHref } from "./link-intent.js";
3
3
  describe("getAnchorFromNavigationEvent", () => {
4
4
  it("returns the anchor when the event target is a text node inside it", () => {
5
5
  const anchor = document.createElement("a");
@@ -31,6 +31,17 @@ describe("getAnchorFromNavigationEvent", () => {
31
31
  expect(getAnchorFromNavigationEvent(event, "a[data-eco-link]")).toBe(anchor);
32
32
  });
33
33
  });
34
+ describe("isStaticAssetHref", () => {
35
+ it("returns true for known static asset paths", () => {
36
+ expect(isStaticAssetHref("/skill.txt")).toBe(true);
37
+ expect(isStaticAssetHref("/skill/reference/full-stack.md")).toBe(true);
38
+ expect(isStaticAssetHref("/assets/logo.png")).toBe(true);
39
+ });
40
+ it("returns false for application routes", () => {
41
+ expect(isStaticAssetHref("/docs/getting-started/introduction")).toBe(false);
42
+ expect(isStaticAssetHref("/")).toBe(false);
43
+ });
44
+ });
34
45
  describe("recoverPendingNavigationHref", () => {
35
46
  it("returns null for stale or missing pending intent state", () => {
36
47
  expect(recoverPendingNavigationHref(null, true, 10)).toBeNull();
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Canonical link interception, prefetch eligibility, and HTML fetch guards.
3
+ * @module
4
+ */
5
+ export type LinkNavigationPolicyOptions = {
6
+ /** Attribute that forces a full document reload instead of SPA navigation. */
7
+ reloadAttribute?: string;
8
+ /** Attribute that opts a link out of prefetch. */
9
+ noPrefetchAttribute?: string;
10
+ };
11
+ export type LinkNavigationDecision = {
12
+ shouldIntercept: true;
13
+ href: string;
14
+ } | {
15
+ shouldIntercept: false;
16
+ reason: 'modified-click' | 'non-left-click' | 'external-target' | 'explicit-reload' | 'download' | 'no-prefetch' | 'invalid-href' | 'cross-origin' | 'static-asset' | 'same-page-hash';
17
+ };
18
+ /**
19
+ * Returns whether an href only adds a hash fragment on the current page.
20
+ */
21
+ export declare function isSamePageHashNavigationHref(href: string): boolean;
22
+ /**
23
+ * Decides whether a pointer or click on an anchor should be handled as SPA navigation.
24
+ */
25
+ export declare function getLinkNavigationDecision(event: MouseEvent | PointerEvent, link: HTMLAnchorElement, options: LinkNavigationPolicyOptions): LinkNavigationDecision;
26
+ /**
27
+ * Returns the href when SPA navigation should intercept the click, otherwise `null`.
28
+ */
29
+ export declare function getNavigableHrefFromClick(event: MouseEvent | PointerEvent, link: HTMLAnchorElement, options: LinkNavigationPolicyOptions): string | null;
30
+ /**
31
+ * Returns whether a link is eligible for hover or viewport prefetch.
32
+ */
33
+ export declare function shouldPrefetchLink(link: HTMLAnchorElement, options: LinkNavigationPolicyOptions): boolean;
34
+ /**
35
+ * @remarks
36
+ * Prefetch and SPA fetches request HTML. Only explicit `text/html` and
37
+ * `application/xhtml+xml` responses are accepted; missing or asset types are rejected.
38
+ */
39
+ export declare function isHtmlPageResponse(response: Response): boolean;
40
+ /**
41
+ * @remarks
42
+ * Servers should send `text/html`. When the type is missing or the runtime
43
+ * defaults to `text/plain` (common in tests), a document-shaped body is accepted.
44
+ */
45
+ export declare function assertHtmlPageResponse(response: Response): Promise<void>;
@@ -0,0 +1,101 @@
1
+ import { isStaticAssetHref } from "./link-intent.js";
2
+ function getLinkIneligibilityReason(link, options) {
3
+ if (options.noPrefetchAttribute && link.hasAttribute(options.noPrefetchAttribute)) {
4
+ return "no-prefetch";
5
+ }
6
+ if (options.reloadAttribute && link.hasAttribute(options.reloadAttribute)) {
7
+ return "explicit-reload";
8
+ }
9
+ if (link.hasAttribute("download")) {
10
+ return "download";
11
+ }
12
+ const href = link.getAttribute("href");
13
+ if (!href || href.startsWith("#") || href.startsWith("javascript:")) {
14
+ return "invalid-href";
15
+ }
16
+ const url = new URL(href, window.location.origin);
17
+ if (url.origin !== window.location.origin) {
18
+ return "cross-origin";
19
+ }
20
+ if (isStaticAssetHref(href)) {
21
+ return "static-asset";
22
+ }
23
+ return null;
24
+ }
25
+ function isSamePageHashNavigationHref(href) {
26
+ if (!href) {
27
+ return false;
28
+ }
29
+ const currentUrl = new URL(window.location.href);
30
+ const targetUrl = new URL(href, currentUrl);
31
+ return targetUrl.origin === currentUrl.origin && targetUrl.hash.length > 0 && targetUrl.pathname === currentUrl.pathname && targetUrl.search === currentUrl.search;
32
+ }
33
+ function getLinkNavigationDecision(event, link, options) {
34
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
35
+ return { shouldIntercept: false, reason: "modified-click" };
36
+ }
37
+ if (event.button !== 0) {
38
+ return { shouldIntercept: false, reason: "non-left-click" };
39
+ }
40
+ const target = link.getAttribute("target");
41
+ if (target && target !== "_self") {
42
+ return { shouldIntercept: false, reason: "external-target" };
43
+ }
44
+ const ineligibility = getLinkIneligibilityReason(link, options);
45
+ if (ineligibility) {
46
+ return { shouldIntercept: false, reason: ineligibility };
47
+ }
48
+ const href = link.getAttribute("href");
49
+ if (isSamePageHashNavigationHref(href)) {
50
+ return { shouldIntercept: false, reason: "same-page-hash" };
51
+ }
52
+ return { shouldIntercept: true, href };
53
+ }
54
+ function getNavigableHrefFromClick(event, link, options) {
55
+ const decision = getLinkNavigationDecision(event, link, options);
56
+ return decision.shouldIntercept ? decision.href : null;
57
+ }
58
+ function shouldPrefetchLink(link, options) {
59
+ if (getLinkIneligibilityReason(link, options)) {
60
+ return false;
61
+ }
62
+ const href = link.getAttribute("href");
63
+ try {
64
+ const url = new URL(href, window.location.origin);
65
+ const currentPath = window.location.pathname + window.location.search;
66
+ const targetPath = url.pathname + url.search;
67
+ return currentPath !== targetPath;
68
+ } catch {
69
+ return false;
70
+ }
71
+ }
72
+ function isHtmlPageResponse(response) {
73
+ const contentType = response.headers.get("Content-Type");
74
+ if (!contentType) {
75
+ return false;
76
+ }
77
+ const normalized = contentType.split(";")[0]?.trim().toLowerCase() ?? "";
78
+ return normalized === "text/html" || normalized === "application/xhtml+xml";
79
+ }
80
+ async function assertHtmlPageResponse(response) {
81
+ if (isHtmlPageResponse(response)) {
82
+ return;
83
+ }
84
+ const contentType = response.headers.get("Content-Type");
85
+ const normalized = contentType?.split(";")[0]?.trim().toLowerCase() ?? "";
86
+ if (normalized === "text/plain" || normalized === "") {
87
+ const sample = (await response.clone().text()).trimStart();
88
+ if (sample.startsWith("<")) {
89
+ return;
90
+ }
91
+ }
92
+ throw new Error(`Expected HTML page response, received ${contentType ?? "unknown content type"}`);
93
+ }
94
+ export {
95
+ assertHtmlPageResponse,
96
+ getLinkNavigationDecision,
97
+ getNavigableHrefFromClick,
98
+ isHtmlPageResponse,
99
+ isSamePageHashNavigationHref,
100
+ shouldPrefetchLink
101
+ };
@@ -0,0 +1,92 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ getLinkNavigationDecision,
4
+ getNavigableHrefFromClick,
5
+ isHtmlPageResponse,
6
+ isSamePageHashNavigationHref,
7
+ shouldPrefetchLink
8
+ } from "./link-navigation-policy.js";
9
+ function createLink(href, attributes = {}) {
10
+ const link = document.createElement("a");
11
+ link.href = href;
12
+ for (const [key, value] of Object.entries(attributes)) {
13
+ link.setAttribute(key, value);
14
+ }
15
+ document.body.append(link);
16
+ return link;
17
+ }
18
+ function createMouseEvent(button = 0, modifiers = {}) {
19
+ return new MouseEvent("click", { bubbles: true, cancelable: true, button, ...modifiers });
20
+ }
21
+ const policy = { reloadAttribute: "data-eco-reload", noPrefetchAttribute: "data-eco-no-prefetch" };
22
+ describe("isSamePageHashNavigationHref", () => {
23
+ it("returns true for same pathname with a new hash", () => {
24
+ history.replaceState({}, "", "/docs/page");
25
+ expect(isSamePageHashNavigationHref("/docs/page#section")).toBe(true);
26
+ });
27
+ });
28
+ describe("getLinkNavigationDecision", () => {
29
+ it("allows same-origin page links", () => {
30
+ const link = createLink("/page");
31
+ const decision = getLinkNavigationDecision(createMouseEvent(), link, policy);
32
+ expect(decision).toEqual({ shouldIntercept: true, href: "/page" });
33
+ });
34
+ it("rejects static asset links", () => {
35
+ const link = createLink("/skill.txt");
36
+ expect(getLinkNavigationDecision(createMouseEvent(), link, policy)).toEqual({
37
+ shouldIntercept: false,
38
+ reason: "static-asset"
39
+ });
40
+ });
41
+ it("rejects explicit reload links", () => {
42
+ const link = createLink("/logout", { "data-eco-reload": "" });
43
+ expect(getLinkNavigationDecision(createMouseEvent(), link, policy)).toEqual({
44
+ shouldIntercept: false,
45
+ reason: "explicit-reload"
46
+ });
47
+ });
48
+ });
49
+ describe("getNavigableHrefFromClick", () => {
50
+ it("returns href when navigation should be intercepted", () => {
51
+ const link = createLink("/page");
52
+ expect(getNavigableHrefFromClick(createMouseEvent(), link, policy)).toBe("/page");
53
+ });
54
+ it("returns null for static assets", () => {
55
+ const link = createLink("/llms.txt");
56
+ expect(getNavigableHrefFromClick(createMouseEvent(), link, policy)).toBeNull();
57
+ });
58
+ });
59
+ describe("shouldPrefetchLink", () => {
60
+ it("prefetches internal page links", () => {
61
+ history.replaceState({}, "", "/");
62
+ const link = createLink("/docs/intro");
63
+ expect(shouldPrefetchLink(link, policy)).toBe(true);
64
+ });
65
+ it("skips static assets and opt-outs", () => {
66
+ history.replaceState({}, "", "/");
67
+ expect(shouldPrefetchLink(createLink("/skill.txt"), policy)).toBe(false);
68
+ expect(shouldPrefetchLink(createLink("/page", { "data-eco-no-prefetch": "" }), policy)).toBe(false);
69
+ });
70
+ });
71
+ describe("isHtmlPageResponse", () => {
72
+ it("accepts html content types", () => {
73
+ expect(isHtmlPageResponse(new Response("", { headers: { "Content-Type": "text/html; charset=utf-8" } }))).toBe(
74
+ true
75
+ );
76
+ });
77
+ it("rejects plain text responses", () => {
78
+ expect(isHtmlPageResponse(new Response("", { headers: { "Content-Type": "text/plain" } }))).toBe(false);
79
+ });
80
+ });
81
+ describe("isHtmlPageResponse", () => {
82
+ it("accepts html and xhtml content types", () => {
83
+ expect(isHtmlPageResponse(new Response("", { headers: { "Content-Type": "text/html" } }))).toBe(true);
84
+ expect(isHtmlPageResponse(new Response("", { headers: { "Content-Type": "application/xhtml+xml" } }))).toBe(
85
+ true
86
+ );
87
+ });
88
+ it("rejects plain text and markdown responses", () => {
89
+ expect(isHtmlPageResponse(new Response("text", { headers: { "Content-Type": "text/plain" } }))).toBe(false);
90
+ expect(isHtmlPageResponse(new Response("# doc", { headers: { "Content-Type": "text/markdown" } }))).toBe(false);
91
+ });
92
+ });
@@ -96,7 +96,7 @@ export type EcoNavigationRuntimeRegistration = {
96
96
  * Implementations may honor `request.moduleUrl` to force a specific page entry
97
97
  * and `request.clearCache` to discard persisted runtime state before reloading.
98
98
  */
99
- reloadCurrentPage?: (request?: EcoReloadRequest) => Promise<void>;
99
+ reloadCurrentPage?: (request?: EcoReloadRequest) => Promise<boolean | void>;
100
100
  /**
101
101
  * Releases runtime-owned client state before another runtime commits a new
102
102
  * document.
@@ -176,8 +176,8 @@ function createEcoNavigationRuntime() {
176
176
  if (!registration?.reloadCurrentPage) {
177
177
  continue;
178
178
  }
179
- await registration.reloadCurrentPage(request);
180
- return true;
179
+ const handled = await registration.reloadCurrentPage(request);
180
+ return handled !== false;
181
181
  }
182
182
  return false;
183
183
  },
@@ -198,6 +198,16 @@ function createEcoNavigationRuntime() {
198
198
  await runtime.cleanupOwner(owner);
199
199
  }
200
200
  };
201
+ if (typeof window !== "undefined") {
202
+ window.addEventListener("pageshow", (event) => {
203
+ if (event.persisted) {
204
+ runtime.cancelCurrentNavigationTransaction();
205
+ }
206
+ });
207
+ window.addEventListener("pagehide", () => {
208
+ runtime.cancelCurrentNavigationTransaction();
209
+ });
210
+ }
201
211
  return runtime;
202
212
  }
203
213
  function getEcoNavigationRuntime(windowObject = window) {
@@ -45,6 +45,7 @@ export declare function createBrowserRuntimeModuleAsset(options: {
45
45
  fileName: string;
46
46
  cacheDirName?: string;
47
47
  rootDir?: string;
48
+ workDir?: string;
48
49
  bundleOptions?: {
49
50
  define?: Record<string, string>;
50
51
  minify?: boolean;
@@ -31,6 +31,7 @@ function createBrowserRuntimeModuleAsset(options) {
31
31
  modules: options.modules,
32
32
  fileName: `${options.name}-entry.mjs`,
33
33
  rootDir: options.rootDir,
34
+ workDir: options.workDir,
34
35
  cacheDirName: options.cacheDirName
35
36
  });
36
37
  return createBrowserRuntimeScriptAsset({
@@ -9,12 +9,14 @@ export type BrowserRuntimeEntryModuleConfig = {
9
9
  * @remarks
10
10
  * Integrations use this helper when they need a browser runtime asset composed
11
11
  * from multiple bare specifiers but do not want to own temporary file assembly
12
- * logic themselves. The generated file lives under a cache directory inside the
13
- * consuming app root so repeated runs can reuse the same location.
12
+ * logic themselves. The generated file lives under the app work directory so
13
+ * repeated runs can reuse the same location without placing sources inside
14
+ * `node_modules`, which can cause bundlers to externalize bare imports.
14
15
  */
15
16
  export declare function createBrowserRuntimeEntryModule(options: {
16
17
  modules: BrowserRuntimeEntryModuleConfig[];
17
18
  fileName: string;
18
19
  rootDir?: string;
20
+ workDir?: string;
19
21
  cacheDirName?: string;
20
22
  }): string;
@@ -1,37 +1,57 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { createRequire } from "node:module";
4
+ import { DEFAULT_ECOPAGES_WORK_DIR } from "../../../config/constants.js";
4
5
  function createBrowserRuntimeEntryModule(options) {
6
+ if (options.modules.some((module) => !module.specifier.startsWith("node:")) && !options.rootDir) {
7
+ throw new Error("createBrowserRuntimeEntryModule requires rootDir to resolve package specifiers");
8
+ }
5
9
  const rootDir = options.rootDir ?? process.cwd();
10
+ const workDir = options.workDir ?? path.join(rootDir, DEFAULT_ECOPAGES_WORK_DIR);
6
11
  const artifactsDir = path.join(
7
- rootDir,
8
- "node_modules",
9
- ".cache",
12
+ workDir,
13
+ ".browser-runtime-entries",
10
14
  options.cacheDirName ?? "ecopages-browser-runtime"
11
15
  );
12
16
  fs.mkdirSync(artifactsDir, { recursive: true });
13
17
  const requireFromRoot = createRequire(path.join(rootDir, "package.json"));
14
18
  const seenExports = /* @__PURE__ */ new Set();
15
19
  const statements = [];
20
+ const filePath = path.join(artifactsDir, options.fileName);
21
+ const entryDir = path.dirname(filePath);
16
22
  for (const module of options.modules) {
23
+ const importSpecifier = resolveEntryImportSpecifier(module.specifier, requireFromRoot, entryDir);
17
24
  if (module.defaultExport) {
18
- statements.push(`import __ecopages_default_export__ from '${module.specifier}';`);
25
+ statements.push(`import __ecopages_default_export__ from '${importSpecifier}';`);
19
26
  statements.push("export default __ecopages_default_export__;");
20
27
  }
21
28
  const exportNames = getModuleExportNames(module.specifier, requireFromRoot).filter(
22
29
  (name) => !seenExports.has(name)
23
30
  );
24
31
  if (exportNames.length > 0) {
25
- statements.push(`export { ${exportNames.join(", ")} } from '${module.specifier}';`);
32
+ statements.push(`export { ${exportNames.join(", ")} } from '${importSpecifier}';`);
26
33
  for (const exportName of exportNames) {
27
34
  seenExports.add(exportName);
28
35
  }
29
36
  }
30
37
  }
31
- const filePath = path.join(artifactsDir, options.fileName);
32
- fs.writeFileSync(filePath, statements.join("\n"), "utf-8");
38
+ const content = statements.join("\n");
39
+ if (!fs.existsSync(filePath) || fs.readFileSync(filePath, "utf-8") !== content) {
40
+ fs.writeFileSync(filePath, content, "utf-8");
41
+ }
33
42
  return filePath;
34
43
  }
44
+ function resolveEntryImportSpecifier(specifier, requireFromRoot, entryDir) {
45
+ if (specifier.startsWith("node:") || specifier.startsWith("file:")) {
46
+ return specifier;
47
+ }
48
+ const resolvedPath = requireFromRoot.resolve(specifier);
49
+ let relativePath = path.relative(entryDir, resolvedPath).replace(/\\/g, "/");
50
+ if (!relativePath.startsWith(".")) {
51
+ relativePath = `./${relativePath}`;
52
+ }
53
+ return relativePath;
54
+ }
35
55
  function getModuleExportNames(specifier, requireFromRoot) {
36
56
  const moduleExports = requireFromRoot(specifier);
37
57
  return Object.keys(moduleExports).filter((name) => name !== "__esModule" && name !== "default").filter((name) => /^[$A-Z_a-z][$\w]*$/.test(name)).sort();
@@ -1,6 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { RESOLVED_ASSETS_DIR } from "../../../../../config/constants.js";
3
3
  import { fileSystem } from "@ecopages/file-system";
4
+ import { appLogger } from "../../../../../global/app-logger.js";
4
5
  import { BaseScriptProcessor } from "../base/base-script-processor.js";
5
6
  class FileScriptProcessor extends BaseScriptProcessor {
6
7
  hmrManager;
@@ -20,20 +21,41 @@ class FileScriptProcessor extends BaseScriptProcessor {
20
21
  }
21
22
  async process(dep) {
22
23
  if (this.hmrManager?.isEnabled() && !dep.inline) {
23
- const outputUrl = await this.hmrManager.registerScriptEntrypoint(dep.filepath);
24
- const outputFilepath = this.resolveHmrOutputFilepath(dep.filepath);
25
- return {
26
- filepath: outputFilepath ?? dep.filepath,
27
- sourceFilepath: dep.filepath,
28
- srcUrl: outputUrl,
29
- kind: "script",
30
- position: dep.position,
31
- attributes: dep.attributes,
32
- inline: false,
33
- excludeFromHtml: dep.excludeFromHtml,
34
- packageRole: dep.packageRole,
35
- bundledSourceFilepaths: dep.bundledSourceFilepaths
36
- };
24
+ const resolvedOutput = this.hmrManager.getResolvedScriptOutput?.(dep.filepath);
25
+ if (resolvedOutput) {
26
+ return {
27
+ filepath: resolvedOutput.outputPath,
28
+ sourceFilepath: dep.filepath,
29
+ srcUrl: resolvedOutput.outputUrl,
30
+ kind: "script",
31
+ position: dep.position,
32
+ attributes: dep.attributes,
33
+ inline: false,
34
+ excludeFromHtml: dep.excludeFromHtml,
35
+ packageRole: dep.packageRole,
36
+ bundledSourceFilepaths: dep.bundledSourceFilepaths
37
+ };
38
+ }
39
+ try {
40
+ const outputUrl = await this.hmrManager.registerScriptEntrypoint(dep.filepath);
41
+ const outputFilepath = this.resolveHmrOutputFilepath(dep.filepath);
42
+ return {
43
+ filepath: outputFilepath ?? dep.filepath,
44
+ sourceFilepath: dep.filepath,
45
+ srcUrl: outputUrl,
46
+ kind: "script",
47
+ position: dep.position,
48
+ attributes: dep.attributes,
49
+ inline: false,
50
+ excludeFromHtml: dep.excludeFromHtml,
51
+ packageRole: dep.packageRole,
52
+ bundledSourceFilepaths: dep.bundledSourceFilepaths
53
+ };
54
+ } catch (error) {
55
+ appLogger.warn(
56
+ `[FileScriptProcessor] HMR script registration failed for ${dep.filepath}, falling back to non-HMR asset: ${error instanceof Error ? error.message : String(error)}`
57
+ );
58
+ }
37
59
  }
38
60
  const content = fileSystem.readFileSync(dep.filepath);
39
61
  const shouldBundle = this.shouldBundle(dep);
@@ -8,19 +8,33 @@ class NodeModuleScriptProcessor extends BaseScriptProcessor {
8
8
  const modulePath = this.resolveModulePath(dep.importPath, this.appConfig.rootDir);
9
9
  const moduleName = path.basename(modulePath);
10
10
  const filename = dep.name ?? `nm-${moduleName}`;
11
+ const shouldBundle = this.shouldBundle(dep);
11
12
  const configHash = this.generateHash(
12
- JSON.stringify({ inline: dep.inline, minify: !dep.inline && this.isProduction, opts: dep.bundleOptions })
13
+ JSON.stringify({
14
+ bundle: shouldBundle,
15
+ inline: dep.inline,
16
+ minify: shouldBundle && !dep.inline && this.isProduction,
17
+ opts: dep.bundleOptions
18
+ })
13
19
  );
14
20
  const cachekey = `${this.buildCacheKey(filename, this.generateHash(modulePath), dep)}:${configHash}`;
15
21
  return this.getOrProcess(cachekey, async () => {
16
- if (dep.inline) {
22
+ if (dep.inline || !shouldBundle) {
17
23
  const content = fileSystem.readFileAsBuffer(modulePath).toString();
24
+ const filepath = dep.inline ? void 0 : path.join(this.getAssetsDir(), "vendors", filename);
25
+ if (filepath) {
26
+ fileSystem.ensureDir(path.dirname(filepath));
27
+ if (!fileSystem.exists(filepath) || fileSystem.readFileAsBuffer(filepath).toString() !== content) {
28
+ fileSystem.copyFile(modulePath, filepath);
29
+ }
30
+ }
18
31
  return {
32
+ filepath,
19
33
  content,
20
34
  kind: dep.kind,
21
35
  position: dep.position,
22
36
  attributes: dep.attributes,
23
- inline: true,
37
+ inline: dep.inline,
24
38
  excludeFromHtml: dep.excludeFromHtml,
25
39
  packageRole: dep.packageRole,
26
40
  bundledSourceFilepaths: dep.bundledSourceFilepaths
@@ -1,8 +1,5 @@
1
1
  import type { ContentStylesheetAsset, ProcessedAsset } from '../../assets.types.js';
2
2
  import { BaseProcessor } from '../base/base-processor.js';
3
3
  export declare class ContentStylesheetProcessor extends BaseProcessor<ContentStylesheetAsset> {
4
- private static readonly PROCESSABLE_STYLESHEET_EXTENSIONS;
5
- private isProcessableStylesheet;
6
- private applyStylesheetProcessors;
7
4
  process(dep: ContentStylesheetAsset): Promise<ProcessedAsset>;
8
5
  }
@@ -1,40 +1,11 @@
1
1
  import path from "node:path";
2
2
  import { fileSystem } from "@ecopages/file-system";
3
3
  import { BaseProcessor } from "../base/base-processor.js";
4
+ import { applyStylesheetProcessors } from "./stylesheet-processor-pipeline.js";
4
5
  class ContentStylesheetProcessor extends BaseProcessor {
5
- static PROCESSABLE_STYLESHEET_EXTENSIONS = /* @__PURE__ */ new Set([".css", ".scss", ".sass", ".less"]);
6
- isProcessableStylesheet(filepath) {
7
- return ContentStylesheetProcessor.PROCESSABLE_STYLESHEET_EXTENSIONS.has(path.extname(filepath));
8
- }
9
- async applyStylesheetProcessors(content, filepath) {
10
- if (!this.isProcessableStylesheet(filepath)) {
11
- return content;
12
- }
13
- let transformedContent = content;
14
- const processors = this.appConfig.processors ? Array.from(this.appConfig.processors.values()) : [];
15
- for (const processor of processors) {
16
- const hasCapabilities = processor.getAssetCapabilities().length > 0;
17
- const canProcessStylesheet = processor.canProcessAsset("stylesheet", filepath);
18
- if (!canProcessStylesheet && (hasCapabilities || !processor.getName().includes("postcss"))) {
19
- continue;
20
- }
21
- if (!processor.matchesFileFilter(filepath)) {
22
- continue;
23
- }
24
- const result = await processor.process(transformedContent, filepath);
25
- if (typeof result === "string") {
26
- transformedContent = result;
27
- continue;
28
- }
29
- if (result instanceof Buffer) {
30
- transformedContent = result.toString();
31
- }
32
- }
33
- return transformedContent;
34
- }
35
6
  async process(dep) {
36
7
  const virtualFilepath = path.join(this.appConfig.absolutePaths.distDir, "styles", "page-bundle.css");
37
- const processedContent = await this.applyStylesheetProcessors(dep.content, virtualFilepath);
8
+ const processedContent = await applyStylesheetProcessors(this.appConfig, dep.content, virtualFilepath);
38
9
  const hash = this.generateHash(processedContent);
39
10
  const filename = `style-${hash}.css`;
40
11
  const cachekey = this.buildCacheKey(filename, hash, dep);
@@ -1,9 +1,6 @@
1
1
  import type { FileStylesheetAsset, ProcessedAsset } from '../../assets.types.js';
2
2
  import { BaseProcessor } from '../base/base-processor.js';
3
3
  export declare class FileStylesheetProcessor extends BaseProcessor<FileStylesheetAsset> {
4
- private static readonly PROCESSABLE_STYLESHEET_EXTENSIONS;
5
4
  getStyleContent: (srcUrl: string) => Buffer;
6
- private isProcessableStylesheet;
7
- private applyStylesheetProcessors;
8
5
  process(dep: FileStylesheetAsset): Promise<ProcessedAsset>;
9
6
  }