@ecopages/core 0.2.0-beta.31 → 0.2.0-beta.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +82 -3
- package/src/adapters/abstract/application-adapter.d.ts +0 -8
- package/src/adapters/abstract/application-adapter.js +252 -261
- package/src/adapters/abstract/router-adapter.js +12 -4
- package/src/adapters/abstract/segment-path-matcher.js +132 -134
- package/src/adapters/abstract/server-adapter.js +22 -14
- package/src/adapters/abstract/ws-pattern-matcher.js +24 -26
- package/src/adapters/bun/bun-user-websocket-lifecycle.js +83 -86
- package/src/adapters/bun/client-bridge.js +51 -50
- package/src/adapters/bun/create-app.js +171 -161
- package/src/adapters/bun/hmr-manager.js +62 -57
- package/src/adapters/bun/index.js +2 -31
- package/src/adapters/bun/runtime-host.js +61 -54
- package/src/adapters/bun/server-adapter.d.ts +0 -1
- package/src/adapters/bun/server-adapter.js +561 -561
- package/src/adapters/bun/static-preview-host.js +43 -46
- package/src/adapters/create-app.js +11 -14
- package/src/adapters/index.js +2 -30
- package/src/adapters/node/create-app.js +123 -129
- package/src/adapters/node/http-request-bridge.js +147 -106
- package/src/adapters/node/node-client-bridge.js +62 -64
- package/src/adapters/node/node-hmr-manager.js +58 -48
- package/src/adapters/node/runtime-host.js +96 -89
- package/src/adapters/node/server-adapter-dependencies.js +18 -21
- package/src/adapters/node/server-adapter.d.ts +0 -1
- package/src/adapters/node/server-adapter.js +347 -326
- package/src/adapters/node/static-content-server.js +172 -191
- package/src/adapters/node/static-preview-host.js +87 -78
- package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.d.ts +16 -0
- package/src/adapters/shared/hmr/dev-transform-entrypoint-registry.js +32 -0
- package/src/adapters/shared/hmr/hmr-html-response.js +46 -32
- package/src/adapters/shared/hmr/shared-hmr-manager.d.ts +29 -38
- package/src/adapters/shared/hmr/shared-hmr-manager.js +439 -425
- package/src/adapters/shared/http/api-request-pipeline.js +122 -119
- package/src/adapters/shared/http/api-response.js +77 -77
- package/src/adapters/shared/http/create-body-response.js +22 -19
- package/src/adapters/shared/http/define-api-handler.js +41 -55
- package/src/adapters/shared/http/explicit-static-render-preparation.js +26 -23
- package/src/adapters/shared/http/explicit-static-route-matcher.js +55 -62
- package/src/adapters/shared/http/file-route-middleware-pipeline.js +81 -75
- package/src/adapters/shared/http/fs-server-response-factory.js +101 -96
- package/src/adapters/shared/http/fs-server-response-matcher.js +239 -257
- package/src/adapters/shared/http/run-middleware-chain.js +15 -15
- package/src/adapters/shared/runtime/application-adapter.js +72 -87
- package/src/adapters/shared/runtime/copy-runtime-public-dir.js +25 -22
- package/src/adapters/shared/runtime/port-manager.js +207 -177
- package/src/adapters/shared/runtime/render-context.js +94 -79
- package/src/adapters/shared/runtime/runtime-app-bootstrap.js +33 -38
- package/src/adapters/shared/runtime/runtime-host.js +1 -0
- package/src/adapters/shared/runtime/runtime-server-lifecycle.d.ts +2 -3
- package/src/adapters/shared/runtime/runtime-server-lifecycle.js +65 -51
- package/src/adapters/shared/runtime/server-adapter.d.ts +1 -0
- package/src/adapters/shared/runtime/server-adapter.js +233 -217
- package/src/adapters/shared/runtime/server-route-handler.js +74 -70
- package/src/adapters/shared/runtime/server-static-builder.js +165 -184
- package/src/adapters/shared/runtime/static-preview-host.js +1 -0
- package/src/adapters/shared/ws/node-http-websocket-upgrades.js +97 -88
- package/src/adapters/shared/ws/websocket-lifecycle.js +57 -50
- package/src/build/README.md +52 -1
- package/src/build/app-build-manifest-runtime.js +129 -108
- package/src/build/browser/browser-runtime-manifest.js +47 -50
- package/src/build/browser/browser-runtime-plugin-helpers.js +32 -12
- package/src/build/browser/browser-runtime-plugin.js +206 -160
- package/src/build/browser/jsx-ownership-plugins.js +51 -37
- package/src/build/browser/lit-static-render-worker-context.js +3 -5
- package/src/build/build-adapter.js +276 -139
- package/src/build/cache/build-input-fingerprint.js +28 -26
- package/src/build/cache/cache-constants.js +4 -6
- package/src/build/cache/cache-keys.js +64 -56
- package/src/build/cache/pages-unified-graph-build.js +179 -178
- package/src/build/cache/production-build-cache.js +47 -54
- package/src/build/cache/server-entry-build-cache.js +134 -146
- package/src/build/contracts/build-contracts.d.ts +4 -4
- package/src/build/contracts/build-contracts.js +1 -0
- package/src/build/contracts/build-manifest.js +58 -43
- package/src/build/contracts/build-types.js +1 -0
- package/src/build/rolldown/rolldown-adapter-helpers.d.ts +9 -0
- package/src/build/rolldown/rolldown-adapter-helpers.js +405 -298
- package/src/build/rolldown/rolldown-build-adapter.js +53 -61
- package/src/build/rolldown/rolldown-build-invocation-metrics.js +31 -35
- package/src/build/rolldown/rolldown-plugin-bridge.js +264 -194
- package/src/build/rolldown/rolldown-source-transform-pass.js +59 -50
- package/src/build/rolldown/runtime-build-output-normalizer.js +227 -175
- package/src/build/rolldown/server-side-css-shim-plugin.js +54 -31
- package/src/build/runtime/build-profile-options.js +36 -36
- package/src/build/runtime/build-request-identity.js +70 -45
- package/src/build/runtime/build-request-policy.js +80 -42
- package/src/build/runtime/build-runtime.js +64 -54
- package/src/build/runtime/deduping-build-executor.js +38 -32
- package/src/build/runtime/parallel-build-executor.js +58 -50
- package/src/build/runtime/serialized-build-executor.js +78 -62
- package/src/cache/index.js +6 -6
- package/src/cache/module-parse-cache.js +99 -68
- package/src/client/navigation-scripts/classification.js +45 -37
- package/src/client/navigation-scripts/index.js +3 -23
- package/src/client/navigation-scripts/navigation-scripts.test.browser.js +58 -62
- package/src/client/navigation-scripts/registry.js +15 -11
- package/src/client/navigation-scripts/rerun-queue.js +81 -64
- package/src/client/scroll.js +32 -24
- package/src/client/scroll.test.browser.js +27 -33
- package/src/client/view-transitions.js +68 -55
- package/src/config/config-builder.d.ts +8 -0
- package/src/config/config-builder.js +637 -642
- package/src/config/constants.js +44 -24
- package/src/declarations.d.ts +1 -0
- package/src/dev/README.md +24 -0
- package/src/dev/client-bridge-registry.js +7 -12
- package/src/dev/dev-client-ownership.js +7 -5
- package/src/dev/hmr-manager-registry.js +7 -12
- package/src/dev/host-runtime.js +25 -30
- package/src/dev/runtime-server-started-message.js +2 -5
- package/src/dev/sc-server.js +106 -103
- package/src/dev/transform-server/dev-transform-bundler.d.ts +24 -0
- package/src/dev/transform-server/dev-transform-bundler.js +79 -0
- package/src/dev/transform-server/dev-transform-externalize-plugin.d.ts +12 -0
- package/src/dev/transform-server/dev-transform-externalize-plugin.js +29 -0
- package/src/dev/transform-server/dev-transform-import-rewriter.d.ts +16 -0
- package/src/dev/transform-server/dev-transform-import-rewriter.js +110 -0
- package/src/dev/transform-server/dev-transform-module-kind.d.ts +13 -0
- package/src/dev/transform-server/dev-transform-module-kind.js +17 -0
- package/src/dev/transform-server/dev-transform-runtime-specifiers.d.ts +5 -0
- package/src/dev/transform-server/dev-transform-runtime-specifiers.js +16 -0
- package/src/dev/transform-server/dev-transform-server.d.ts +41 -0
- package/src/dev/transform-server/dev-transform-server.js +191 -0
- package/src/dev/transform-server/dev-transform-url.d.ts +13 -0
- package/src/dev/transform-server/dev-transform-url.js +58 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.d.ts +27 -0
- package/src/dev/transform-server/dev-transform-vendor-registry.js +145 -0
- package/src/dev/transform-server/index.d.ts +6 -0
- package/src/dev/transform-server/index.js +4 -0
- package/src/dev/transform-server/types.d.ts +15 -0
- package/src/dev/transform-server/types.js +1 -0
- package/src/dev-toolbar/README.md +51 -0
- package/src/dev-toolbar/define-dev-tool.d.ts +21 -0
- package/src/dev-toolbar/define-dev-tool.js +9 -0
- package/src/dev-toolbar/dev-toolbar-config.d.ts +25 -0
- package/src/dev-toolbar/dev-toolbar-config.js +19 -0
- package/src/dev-toolbar/dev-toolbar-host.d.ts +49 -0
- package/src/dev-toolbar/dev-toolbar-host.js +117 -0
- package/src/dev-toolbar/dev-toolbar-html-response.d.ts +11 -0
- package/src/dev-toolbar/dev-toolbar-html-response.js +14 -0
- package/src/dev-toolbar/dev-toolbar-manifest.d.ts +15 -0
- package/src/dev-toolbar/dev-toolbar-manifest.js +52 -0
- package/src/dev-toolbar/dev-toolbar-package.d.ts +31 -0
- package/src/dev-toolbar/dev-toolbar-package.js +72 -0
- package/src/dev-toolbar/dev-toolbar-runtime-paths.d.ts +10 -0
- package/src/dev-toolbar/dev-toolbar-runtime-paths.js +13 -0
- package/src/diagnostics/request-build-dedupe.js +34 -28
- package/src/diagnostics/startup-trace.d.ts +10 -1
- package/src/diagnostics/startup-trace.js +269 -241
- package/src/eco/README.md +6 -0
- package/src/eco/eco-declared-component.js +19 -14
- package/src/eco/eco.browser.js +56 -69
- package/src/eco/eco.js +156 -96
- package/src/eco/eco.types.js +5 -0
- package/src/eco/eco.utils.js +1 -10
- package/src/eco/global-injector-map.js +55 -41
- package/src/eco/lazy-injector-map.js +90 -60
- package/src/eco/module-dependencies.js +52 -44
- package/src/eco/page-layout-normalization.js +59 -49
- package/src/errors/http-error.js +50 -49
- package/src/errors/index.js +3 -12
- package/src/errors/locals-access-error.js +5 -8
- package/src/errors/undeclared-component-dependency-error.js +15 -15
- package/src/global/app-logger.js +3 -6
- package/src/hmr/README.md +10 -0
- package/src/hmr/client/hmr-runtime.js +117 -113
- package/src/hmr/client/module-update.d.ts +13 -0
- package/src/hmr/client/module-update.js +39 -0
- package/src/hmr/hmr-asset-paths.d.ts +14 -0
- package/src/hmr/hmr-asset-paths.js +22 -0
- package/src/hmr/hmr-entrypoint-output.d.ts +11 -39
- package/src/hmr/hmr-entrypoint-output.js +28 -54
- package/src/hmr/hmr-file-change-prep.js +13 -16
- package/src/hmr/hmr-registered-entrypoints-context.d.ts +7 -0
- package/src/hmr/hmr-registered-entrypoints-context.js +1 -0
- package/src/hmr/hmr-runtime-paths.d.ts +10 -0
- package/src/hmr/hmr-runtime-paths.js +13 -0
- package/src/hmr/hmr-strategy.d.ts +5 -11
- package/src/hmr/hmr-strategy.js +113 -61
- package/src/hmr/strategies/default-hmr-strategy.js +55 -33
- package/src/hmr/strategies/js-hmr-strategy.d.ts +6 -95
- package/src/hmr/strategies/js-hmr-strategy.js +104 -217
- package/src/hmr/strategies/server-rendered-template-hmr-strategy.js +33 -27
- package/src/index.browser.js +1 -4
- package/src/index.d.ts +1 -0
- package/src/index.js +6 -47
- package/src/integrations/ghtml/ghtml-renderer.js +12 -7
- package/src/integrations/ghtml/ghtml.constants.js +1 -4
- package/src/integrations/ghtml/ghtml.plugin.js +9 -12
- package/src/islands/README.md +25 -0
- package/src/islands/island-host.d.ts +37 -0
- package/src/islands/island-host.js +55 -0
- package/src/plugins/alias-resolver-cache.js +114 -97
- package/src/plugins/alias-resolver-plugin.js +33 -47
- package/src/plugins/define-integration.js +18 -17
- package/src/plugins/eco-component-meta-plugin.js +387 -145
- package/src/plugins/foreign-jsx-override-plugin.js +49 -35
- package/src/plugins/integration-plugin.js +212 -203
- package/src/plugins/jsx-import-source.utils.js +16 -21
- package/src/plugins/processor.js +159 -149
- package/src/plugins/runtime-capability.js +1 -0
- package/src/plugins/source-transform.js +109 -79
- package/src/plugins/tsconfig-import-resolver.d.ts +17 -0
- package/src/plugins/tsconfig-import-resolver.js +218 -142
- package/src/route-renderer/README.md +12 -1
- package/src/route-renderer/orchestration/document-shell/document-shell-render.service.js +143 -136
- package/src/route-renderer/orchestration/document-shell/layout-shell-props.service.js +33 -27
- package/src/route-renderer/orchestration/foreign-child/component-render-context.js +173 -126
- package/src/route-renderer/orchestration/foreign-child/foreign-child-output.utils.js +165 -154
- package/src/route-renderer/orchestration/foreign-child/foreign-subtree-execution.service.js +329 -341
- package/src/route-renderer/orchestration/foreign-child/owning-renderer-resolution.js +33 -32
- package/src/route-renderer/orchestration/integration-renderer.d.ts +1 -0
- package/src/route-renderer/orchestration/integration-renderer.js +731 -750
- package/src/route-renderer/orchestration/integration-renderer.test-fixtures.js +168 -172
- package/src/route-renderer/orchestration/ownership-graph/component-graph-collectors.js +119 -131
- package/src/route-renderer/orchestration/ownership-graph/component-graph.js +82 -91
- package/src/route-renderer/orchestration/ownership-graph/ownership-validation.service.js +82 -76
- package/src/route-renderer/orchestration/page-browser-graph/global-injector-assets.service.js +29 -31
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-contribution.loader.js +10 -6
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-merge.utils.js +31 -28
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.d.ts +3 -0
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js +430 -413
- package/src/route-renderer/orchestration/page-browser-graph/page-browser-graph.service.js +191 -212
- package/src/route-renderer/orchestration/page-browser-graph/processed-asset-dedupe.js +20 -24
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.d.ts +4 -0
- package/src/route-renderer/orchestration/route-pipeline/integration-route-render-adapter.js +24 -24
- package/src/route-renderer/orchestration/route-pipeline/marker-artifact.utils.js +22 -24
- package/src/route-renderer/orchestration/route-pipeline/robots-meta.contribution.js +41 -34
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.d.ts +5 -0
- package/src/route-renderer/orchestration/route-pipeline/route-html-finalization.service.js +42 -22
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.builder.js +50 -48
- package/src/route-renderer/orchestration/route-pipeline/route-prepared-options.utils.js +29 -32
- package/src/route-renderer/orchestration/route-pipeline/route-render-orchestrator.js +150 -156
- package/src/route-renderer/orchestration/string-markup-renderer.js +48 -51
- package/src/route-renderer/page-loading/component-dependency-collection.js +120 -124
- package/src/route-renderer/page-loading/declared-asset-collection.js +66 -94
- package/src/route-renderer/page-loading/dependency-resolver.js +101 -104
- package/src/route-renderer/page-loading/ecopages-virtual-imports.js +68 -54
- package/src/route-renderer/page-loading/lazy-entry-collection.js +75 -98
- package/src/route-renderer/page-loading/lazy-trigger-planning.js +46 -36
- package/src/route-renderer/page-loading/module-declaration-aggregation.js +38 -31
- package/src/route-renderer/page-loading/module-declaration-scripts.js +10 -15
- package/src/route-renderer/page-loading/page-dependency-bundling.js +156 -113
- package/src/route-renderer/page-loading/page-module-loader.js +130 -124
- package/src/route-renderer/route-renderer.js +74 -76
- package/src/router/client/link-intent.js +55 -39
- package/src/router/client/link-intent.test.browser.js +49 -49
- package/src/router/client/link-navigation-policy.js +114 -93
- package/src/router/client/link-navigation-policy.test.browser.js +70 -80
- package/src/router/client/navigation-coordinator.js +235 -211
- package/src/router/server/route-registry.js +237 -242
- package/src/services/assets/asset-processing-service/asset-dependency-keys.js +64 -53
- package/src/services/assets/asset-processing-service/asset-processing.service.js +287 -303
- package/src/services/assets/asset-processing-service/asset.factory.js +78 -81
- package/src/services/assets/asset-processing-service/assets.types.js +1 -0
- package/src/services/assets/asset-processing-service/browser-runtime-asset.factory.js +57 -46
- package/src/services/assets/asset-processing-service/browser-runtime-entry-resolution.js +106 -69
- package/src/services/assets/asset-processing-service/browser-runtime-entry.factory.js +53 -53
- package/src/services/assets/asset-processing-service/finalize-processed-asset.js +4 -6
- package/src/services/assets/asset-processing-service/grouped-content-bundles.js +79 -80
- package/src/services/assets/asset-processing-service/index.js +8 -8
- package/src/services/assets/asset-processing-service/inline-content-script-body.js +22 -16
- package/src/services/assets/asset-processing-service/materialize-content-script-asset.js +21 -17
- package/src/services/assets/asset-processing-service/page-package.js +70 -72
- package/src/services/assets/asset-processing-service/processor.interface.js +7 -5
- package/src/services/assets/asset-processing-service/processor.registry.js +11 -14
- package/src/services/assets/asset-processing-service/processors/base/base-processor.js +59 -62
- package/src/services/assets/asset-processing-service/processors/base/base-script-processor.js +102 -117
- package/src/services/assets/asset-processing-service/processors/index.js +5 -5
- package/src/services/assets/asset-processing-service/processors/script/content-script.processor.js +110 -115
- package/src/services/assets/asset-processing-service/processors/script/file-script.processor.js +95 -97
- package/src/services/assets/asset-processing-service/processors/script/node-module-script.processor.js +136 -135
- package/src/services/assets/asset-processing-service/processors/stylesheet/content-stylesheet.processor.js +27 -29
- package/src/services/assets/asset-processing-service/processors/stylesheet/file-stylesheet.processor.js +34 -40
- package/src/services/assets/asset-processing-service/processors/stylesheet/stylesheet-processor-pipeline.js +36 -35
- package/src/services/assets/asset-processing-service/resolve-integration-plugin.js +14 -16
- package/src/services/assets/asset-processing-service/ungrouped-dependency-processing.js +27 -39
- package/src/services/assets/browser-bundle.service.js +52 -47
- package/src/services/cache/cache.types.js +5 -0
- package/src/services/cache/index.js +6 -7
- package/src/services/cache/memory-cache-store.js +101 -89
- package/src/services/cache/page-cache-service.js +156 -145
- package/src/services/cache/page-request-cache-coordinator.service.js +102 -102
- package/src/services/html/html-rewriter-provider.service.js +61 -61
- package/src/services/html/html-transformer.service.js +249 -266
- package/src/services/invalidation/development-invalidation.service.js +252 -241
- package/src/services/module-loading/app-module-loader.service.js +1 -0
- package/src/services/module-loading/app-server-module-transpiler.service.js +107 -101
- package/src/services/module-loading/host-module-loader-registry.js +6 -11
- package/src/services/module-loading/module-loading-types.js +1 -0
- package/src/services/module-loading/page-module-import.service.js +235 -243
- package/src/services/module-loading/route-module-build-cache-registry.js +47 -33
- package/src/services/module-loading/route-module-build-cache.js +3 -38
- package/src/services/module-loading/route-module-build-cache.store.js +195 -200
- package/src/services/module-loading/route-module-build-manifest.js +61 -74
- package/src/services/module-loading/route-module-dependency-hasher.js +136 -110
- package/src/services/module-loading/server-module-transpiler.service.js +68 -62
- package/src/services/module-loading/source-module-support.js +15 -7
- package/src/services/runtime-state/dev-graph.service.js +165 -157
- package/src/services/runtime-state/entrypoint-dependency-graph.service.js +85 -76
- package/src/services/runtime-state/server-invalidation-state.service.js +38 -30
- package/src/services/validation/schema-validation-service.js +157 -95
- package/src/services/validation/standard-schema.types.js +1 -0
- package/src/services/validation/validate-standard-schema.js +13 -13
- package/src/static-site-generator/production-page-browser-graph-prebuild.js +35 -19
- package/src/static-site-generator/sitemap-routes.js +33 -28
- package/src/static-site-generator/sitemap.js +38 -29
- package/src/static-site-generator/static-build-invalidation.js +34 -56
- package/src/static-site-generator/static-export-context.js +1 -0
- package/src/static-site-generator/static-site-generator.js +517 -553
- package/src/types/internal-types.d.ts +19 -3
- package/src/types/internal-types.js +1 -0
- package/src/types/public-types.d.ts +16 -46
- package/src/types/public-types.js +1 -0
- package/src/utils/deep-merge.js +39 -26
- package/src/utils/ecopages-route-info.js +27 -23
- package/src/utils/hash.js +3 -6
- package/src/utils/html-escaping.js +21 -8
- package/src/utils/html.js +1 -4
- package/src/utils/invariant.js +11 -10
- package/src/utils/locals-utils.js +32 -22
- package/src/utils/parse-cli-args.js +73 -65
- package/src/utils/path-pattern.js +38 -23
- package/src/utils/path-utils.module.js +8 -11
- package/src/utils/resolve-entry-file.js +70 -38
- package/src/utils/resolve-work-dir.js +23 -27
- package/src/utils/runtime.js +24 -33
- package/src/utils/server-utils.module.js +60 -52
- package/src/utils/static-file-extensions.js +52 -46
- package/src/watchers/project-watcher-ignore.js +16 -15
- package/src/watchers/project-watcher.js +385 -366
- package/src/watchers/project-watcher.test-helpers.js +45 -50
- package/src/adapters/shared/hmr/hmr-entrypoint-registrar.d.ts +0 -56
- package/src/adapters/shared/hmr/hmr-entrypoint-registrar.js +0 -106
- package/src/build/cache/dev-browser-script-cache.d.ts +0 -34
- package/src/build/cache/dev-browser-script-cache.js +0 -90
|
@@ -1,387 +1,406 @@
|
|
|
1
|
-
import path from
|
|
2
|
-
import chokidar, {} from
|
|
3
|
-
import { fileSystem } from
|
|
4
|
-
import { appLogger } from
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
} from
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const regex = new RegExp(`${rootDir}/${srcDir}/.*`);
|
|
63
|
-
for (const key in require.cache) {
|
|
64
|
-
if (regex.test(key)) {
|
|
65
|
-
delete require.cache[key];
|
|
66
|
-
}
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import chokidar, {} from 'chokidar';
|
|
3
|
+
import { fileSystem } from '@ecopages/file-system';
|
|
4
|
+
import { appLogger } from '../global/app-logger.js';
|
|
5
|
+
import { DevelopmentInvalidationService, } from '../services/invalidation/development-invalidation.service.js';
|
|
6
|
+
import { prepareHmrFileChange } from '../hmr/hmr-file-change-prep.js';
|
|
7
|
+
import { getAppPageBrowserGraphSession } from '../route-renderer/orchestration/page-browser-graph/page-browser-graph-session.js';
|
|
8
|
+
import { isRegisteredDevTransformEntrypoint } from '../hmr/hmr-entrypoint-output.js';
|
|
9
|
+
import { resolveInternalExecutionDir } from '../utils/resolve-work-dir.js';
|
|
10
|
+
import { createProjectWatcherIgnorePredicate } from './project-watcher-ignore.js';
|
|
11
|
+
/**
|
|
12
|
+
* ProjectWatcher handles file system changes for hot module replacement (HMR).
|
|
13
|
+
* It uses chokidar to watch for file changes and triggers appropriate actions:
|
|
14
|
+
* - Uncaches modules when files change
|
|
15
|
+
* - Refreshes router routes for page files
|
|
16
|
+
* - Triggers HMR server reload
|
|
17
|
+
* - Handles processor-specific file changes
|
|
18
|
+
*
|
|
19
|
+
* The watcher uses chokidar's built-in debouncing through `awaitWriteFinish`
|
|
20
|
+
* to handle rapid file changes efficiently:
|
|
21
|
+
* - stabilityThreshold: 50ms - Time to wait for writes to stabilize
|
|
22
|
+
* - pollInterval: 50ms - Interval to poll for file changes
|
|
23
|
+
*
|
|
24
|
+
* @class ProjectWatcher
|
|
25
|
+
*/
|
|
26
|
+
export class ProjectWatcher {
|
|
27
|
+
/**
|
|
28
|
+
* Duplicate identical watcher events within this window are ignored.
|
|
29
|
+
*
|
|
30
|
+
* Some editors or save pipelines emit two near-identical filesystem change
|
|
31
|
+
* notifications for the same file. Ecopages should treat those as one logical
|
|
32
|
+
* update so HMR and route refresh work are not repeated unnecessarily.
|
|
33
|
+
*/
|
|
34
|
+
static duplicateChangeWindowMs = 150;
|
|
35
|
+
appConfig;
|
|
36
|
+
refreshRouterRoutesCallback;
|
|
37
|
+
hmrManager;
|
|
38
|
+
bridge;
|
|
39
|
+
hostOwnsDevClient;
|
|
40
|
+
invalidationService;
|
|
41
|
+
changeDebounceMs;
|
|
42
|
+
watcher = null;
|
|
43
|
+
closed = false;
|
|
44
|
+
pendingChangeEvents = new Map();
|
|
45
|
+
changeQueue = Promise.resolve();
|
|
46
|
+
constructor({ config, refreshRouterRoutesCallback, hmrManager, bridge, hostOwnsDevClient, changeDebounceMs, }) {
|
|
47
|
+
this.appConfig = config;
|
|
48
|
+
this.refreshRouterRoutesCallback = refreshRouterRoutesCallback;
|
|
49
|
+
this.hmrManager = hmrManager;
|
|
50
|
+
this.bridge = bridge;
|
|
51
|
+
this.hostOwnsDevClient = hostOwnsDevClient === true;
|
|
52
|
+
const envDebounceMs = process.env.ECOPAGES_WATCH_CHANGE_DEBOUNCE_MS;
|
|
53
|
+
this.changeDebounceMs =
|
|
54
|
+
changeDebounceMs ??
|
|
55
|
+
(envDebounceMs !== undefined && envDebounceMs !== '' ? Number(envDebounceMs) : undefined) ??
|
|
56
|
+
ProjectWatcher.duplicateChangeWindowMs;
|
|
57
|
+
this.invalidationService = new DevelopmentInvalidationService(config);
|
|
58
|
+
this.triggerRouterRefresh = this.triggerRouterRefresh.bind(this);
|
|
59
|
+
this.handleError = this.handleError.bind(this);
|
|
60
|
+
this.handleFileChange = this.handleFileChange.bind(this);
|
|
61
|
+
this.processFileChange = this.processFileChange.bind(this);
|
|
67
62
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Uncaches modules in the source directory to ensure fresh imports.
|
|
65
|
+
* This is necessary for hot module replacement to work correctly.
|
|
66
|
+
* @private
|
|
67
|
+
*/
|
|
68
|
+
uncacheModules() {
|
|
69
|
+
if (typeof require === 'undefined')
|
|
70
|
+
return;
|
|
71
|
+
const { srcDir, rootDir } = this.appConfig;
|
|
72
|
+
const regex = new RegExp(`${rootDir}/${srcDir}/.*`);
|
|
73
|
+
for (const key in require.cache) {
|
|
74
|
+
if (regex.test(key)) {
|
|
75
|
+
delete require.cache[key];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Handles public directory file changes by copying only the changed file.
|
|
83
|
-
* @param filePath - Absolute path of the changed file
|
|
84
|
-
*/
|
|
85
|
-
async handlePublicDirFileChange(filePath) {
|
|
86
|
-
try {
|
|
87
|
-
const relativePath = path.relative(this.appConfig.absolutePaths.publicDir, filePath);
|
|
88
|
-
const destPath = path.join(this.appConfig.absolutePaths.distDir, relativePath);
|
|
89
|
-
if (fileSystem.exists(filePath)) {
|
|
90
|
-
const destDir = path.dirname(destPath);
|
|
91
|
-
fileSystem.ensureDir(destDir);
|
|
92
|
-
await fileSystem.copyFileAsync(filePath, destPath);
|
|
93
|
-
}
|
|
94
|
-
this.requestBrowserReload();
|
|
95
|
-
} catch (error) {
|
|
96
|
-
appLogger.error(`Failed to copy public file: ${error instanceof Error ? error.message : String(error)}`);
|
|
97
|
-
this.requestBrowserReload();
|
|
79
|
+
isRouteSourceFile(filePath) {
|
|
80
|
+
return this.invalidationService.isRouteSourceFile(filePath);
|
|
98
81
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
* Serializes file change handling so that concurrent chokidar events are
|
|
102
|
-
* processed one at a time, preventing overlapping builds and race conditions.
|
|
103
|
-
*/
|
|
104
|
-
enqueueChange(task) {
|
|
105
|
-
const queuedTask = this.changeQueue.then(task, task);
|
|
106
|
-
this.changeQueue = queuedTask.catch(() => void 0);
|
|
107
|
-
return queuedTask;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Handles file changes by uncaching modules, refreshing routes, and delegating appropriately.
|
|
111
|
-
* Follows 5-rule priority:
|
|
112
|
-
* 0. Public directory match? -> copy file and reload
|
|
113
|
-
* 1. additionalWatchPaths match? -> reload
|
|
114
|
-
* 2. Include template source? -> current-page refresh via HMR after processor notifications are deferred
|
|
115
|
-
* 3. Processor-owned asset? -> processor already handled it via notification, skip HMR
|
|
116
|
-
* 4. Otherwise -> HMR strategies
|
|
117
|
-
*
|
|
118
|
-
* Processors that watch a file extension as a dependency (e.g. PostCSS watching
|
|
119
|
-
* .tsx for Tailwind class scanning) are always notified first, but do not
|
|
120
|
-
* prevent the file from flowing through the normal HMR strategy pipeline.
|
|
121
|
-
*
|
|
122
|
-
* Duplicate identical watcher events for the same file are coalesced within a
|
|
123
|
-
* short window before any of the priority rules run.
|
|
124
|
-
* @param rawPath - Path of the changed file
|
|
125
|
-
* @param event - The type of file system event
|
|
126
|
-
*/
|
|
127
|
-
handleFileChange(rawPath, event = "change") {
|
|
128
|
-
if (this.closed) {
|
|
129
|
-
return Promise.resolve();
|
|
82
|
+
isIncludeSourceFile(filePath) {
|
|
83
|
+
return this.invalidationService.isIncludeSourceFile(filePath);
|
|
130
84
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
85
|
+
requestBrowserReload() {
|
|
86
|
+
if (this.hostOwnsDevClient) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.bridge.reload();
|
|
134
90
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Handles public directory file changes by copying only the changed file.
|
|
93
|
+
* @param filePath - Absolute path of the changed file
|
|
94
|
+
*/
|
|
95
|
+
async handlePublicDirFileChange(filePath) {
|
|
96
|
+
try {
|
|
97
|
+
const relativePath = path.relative(this.appConfig.absolutePaths.publicDir, filePath);
|
|
98
|
+
const destPath = path.join(this.appConfig.absolutePaths.distDir, relativePath);
|
|
99
|
+
if (fileSystem.exists(filePath)) {
|
|
100
|
+
const destDir = path.dirname(destPath);
|
|
101
|
+
fileSystem.ensureDir(destDir);
|
|
102
|
+
await fileSystem.copyFileAsync(filePath, destPath);
|
|
103
|
+
}
|
|
104
|
+
this.requestBrowserReload();
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
appLogger.error(`Failed to copy public file: ${error instanceof Error ? error.message : String(error)}`);
|
|
108
|
+
this.requestBrowserReload();
|
|
109
|
+
}
|
|
138
110
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
const plan = this.invalidationService.planFileChange(filePath);
|
|
149
|
-
if (plan.category === "public-asset") {
|
|
150
|
-
await this.handlePublicDirFileChange(filePath);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
this.uncacheModules();
|
|
154
|
-
const resolvedFilePath = path.resolve(filePath);
|
|
155
|
-
const graphPreparation = this.hmrManager.isEnabled() ? prepareHmrFileChange(this.appConfig, resolvedFilePath) : void 0;
|
|
156
|
-
if (plan.refreshRoutes && (event === "unlink" || event === "add")) {
|
|
157
|
-
getAppPageBrowserGraphSession(this.appConfig).invalidateByRouteFile(resolvedFilePath);
|
|
158
|
-
}
|
|
159
|
-
const isRegisteredScriptEdit = isRegisteredScriptEntrypoint(
|
|
160
|
-
this.hmrManager.getWatchedFiles(),
|
|
161
|
-
resolvedFilePath
|
|
162
|
-
);
|
|
163
|
-
if (plan.invalidateServerModules && !isRegisteredScriptEdit) {
|
|
164
|
-
this.invalidationService.invalidateServerModules([filePath]);
|
|
165
|
-
}
|
|
166
|
-
if (plan.refreshRoutes) {
|
|
167
|
-
await this.refreshRouterRoutesCallback();
|
|
168
|
-
}
|
|
169
|
-
if (plan.reloadBrowser) {
|
|
170
|
-
this.requestBrowserReload();
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
const deferProcessorNotifications = plan.category === "include-source" || plan.category === "explicit-server-view" || isRegisteredScriptEdit;
|
|
174
|
-
if (deferProcessorNotifications && plan.delegateToHmr) {
|
|
175
|
-
await this.prewarmBeforeHmr(resolvedFilePath, plan);
|
|
176
|
-
await this.hmrManager.handleFileChange(filePath, {
|
|
177
|
-
graphIdentities: graphPreparation?.affectedGraphIdentities
|
|
178
|
-
});
|
|
179
|
-
await this.notifyProcessors(filePath, event);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
await this.notifyProcessors(filePath, event);
|
|
183
|
-
if (plan.processorHandledAsset) {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
if (plan.delegateToHmr) {
|
|
187
|
-
await this.hmrManager.handleFileChange(filePath, {
|
|
188
|
-
graphIdentities: graphPreparation?.affectedGraphIdentities
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
} catch (error) {
|
|
192
|
-
if (error instanceof Error) {
|
|
193
|
-
this.bridge.error(error.message);
|
|
194
|
-
this.handleError(error);
|
|
195
|
-
}
|
|
111
|
+
/**
|
|
112
|
+
* Serializes file change handling so that concurrent chokidar events are
|
|
113
|
+
* processed one at a time, preventing overlapping builds and race conditions.
|
|
114
|
+
*/
|
|
115
|
+
enqueueChange(task) {
|
|
116
|
+
const queuedTask = this.changeQueue.then(task, task);
|
|
117
|
+
this.changeQueue = queuedTask.catch(() => undefined);
|
|
118
|
+
return queuedTask;
|
|
196
119
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Handles file changes by uncaching modules, refreshing routes, and delegating appropriately.
|
|
122
|
+
* Follows 5-rule priority:
|
|
123
|
+
* 0. Public directory match? -> copy file and reload
|
|
124
|
+
* 1. additionalWatchPaths match? -> reload
|
|
125
|
+
* 2. Include template source? -> current-page refresh via HMR after processor notifications are deferred
|
|
126
|
+
* 3. Processor-owned asset? -> processor already handled it via notification, skip HMR
|
|
127
|
+
* 4. Otherwise -> HMR strategies
|
|
128
|
+
*
|
|
129
|
+
* Processors that watch a file extension as a dependency (e.g. PostCSS watching
|
|
130
|
+
* .tsx for Tailwind class scanning) are always notified first, but do not
|
|
131
|
+
* prevent the file from flowing through the normal HMR strategy pipeline.
|
|
132
|
+
*
|
|
133
|
+
* Duplicate identical watcher events for the same file are coalesced within a
|
|
134
|
+
* short window before any of the priority rules run.
|
|
135
|
+
* @param rawPath - Path of the changed file
|
|
136
|
+
* @param event - The type of file system event
|
|
137
|
+
*/
|
|
138
|
+
handleFileChange(rawPath, event = 'change') {
|
|
139
|
+
if (this.closed) {
|
|
140
|
+
return Promise.resolve();
|
|
141
|
+
}
|
|
142
|
+
const filePath = path.resolve(rawPath);
|
|
143
|
+
if (this.changeDebounceMs === 0) {
|
|
144
|
+
return this.enqueueChange(() => this.processFileChange(filePath, event));
|
|
145
|
+
}
|
|
146
|
+
const existing = this.pendingChangeEvents.get(filePath);
|
|
147
|
+
if (existing) {
|
|
148
|
+
clearTimeout(existing.timer);
|
|
149
|
+
}
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
this.pendingChangeEvents.delete(filePath);
|
|
152
|
+
void this.enqueueChange(() => this.processFileChange(filePath, event));
|
|
153
|
+
}, this.changeDebounceMs);
|
|
154
|
+
this.pendingChangeEvents.set(filePath, { event, timer });
|
|
155
|
+
return Promise.resolve();
|
|
205
156
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
157
|
+
async processFileChange(filePath, event) {
|
|
158
|
+
try {
|
|
159
|
+
const plan = this.invalidationService.planFileChange(filePath);
|
|
160
|
+
if (plan.category === 'public-asset') {
|
|
161
|
+
await this.handlePublicDirFileChange(filePath);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
this.uncacheModules();
|
|
165
|
+
const resolvedFilePath = path.resolve(filePath);
|
|
166
|
+
const graphPreparation = this.hmrManager.isEnabled()
|
|
167
|
+
? prepareHmrFileChange(this.appConfig, resolvedFilePath)
|
|
168
|
+
: undefined;
|
|
169
|
+
if (plan.refreshRoutes && (event === 'unlink' || event === 'add')) {
|
|
170
|
+
getAppPageBrowserGraphSession(this.appConfig).invalidateByRouteFile(resolvedFilePath);
|
|
171
|
+
}
|
|
172
|
+
const isRegisteredDevTransformEdit = isRegisteredDevTransformEntrypoint(this.hmrManager.getRegisteredEntrypoints(), resolvedFilePath);
|
|
173
|
+
if (plan.invalidateServerModules && !isRegisteredDevTransformEdit) {
|
|
174
|
+
this.invalidationService.invalidateServerModules([filePath]);
|
|
175
|
+
}
|
|
176
|
+
if (plan.refreshRoutes) {
|
|
177
|
+
await this.refreshRouterRoutesCallback();
|
|
178
|
+
}
|
|
179
|
+
if (plan.reloadBrowser) {
|
|
180
|
+
this.requestBrowserReload();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const deferProcessorNotifications = plan.category === 'include-source' ||
|
|
184
|
+
plan.category === 'explicit-server-view' ||
|
|
185
|
+
isRegisteredDevTransformEdit;
|
|
186
|
+
if (deferProcessorNotifications && plan.delegateToHmr) {
|
|
187
|
+
await this.prewarmBeforeHmr(resolvedFilePath, plan);
|
|
188
|
+
await this.hmrManager.handleFileChange(filePath, {
|
|
189
|
+
graphIdentities: graphPreparation?.affectedGraphIdentities,
|
|
190
|
+
});
|
|
191
|
+
await this.notifyProcessors(filePath, event);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
await this.notifyProcessors(filePath, event);
|
|
195
|
+
if (plan.processorHandledAsset) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (plan.delegateToHmr) {
|
|
199
|
+
await this.hmrManager.handleFileChange(filePath, {
|
|
200
|
+
graphIdentities: graphPreparation?.affectedGraphIdentities,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
if (error instanceof Error) {
|
|
206
|
+
this.bridge.error(error.message);
|
|
207
|
+
this.handleError(error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
232
210
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const { extensions = [] } = watchConfig;
|
|
246
|
-
if (extensions.length && !extensions.some((ext) => filePath.endsWith(ext))) {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
const handler = this.getProcessorHandler(watchConfig, event);
|
|
250
|
-
if (handler) {
|
|
251
|
-
await handler(ctx);
|
|
252
|
-
}
|
|
211
|
+
/**
|
|
212
|
+
* Re-imports server modules before HMR broadcast so reload/refetch does not
|
|
213
|
+
* race stale in-memory imports or custom-element registry state.
|
|
214
|
+
*/
|
|
215
|
+
async prewarmBeforeHmr(filePath, plan) {
|
|
216
|
+
if (plan.category !== 'include-source' && plan.category !== 'explicit-server-view') {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const modulePaths = plan.category === 'include-source'
|
|
220
|
+
? [this.appConfig.absolutePaths.htmlTemplatePath]
|
|
221
|
+
: [path.resolve(filePath)];
|
|
222
|
+
await this.prewarmServerModuleImports(modulePaths, { scope: 'server template' });
|
|
253
223
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
224
|
+
async prewarmServerModuleImports(modulePaths, options) {
|
|
225
|
+
const appModuleLoader = this.appConfig.runtime?.appModuleLoader;
|
|
226
|
+
if (!appModuleLoader) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const outdir = path.join(resolveInternalExecutionDir(this.appConfig), '.server-modules');
|
|
230
|
+
for (const modulePath of modulePaths) {
|
|
231
|
+
if (!modulePath) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
await appModuleLoader.importModule({
|
|
236
|
+
filePath: modulePath,
|
|
237
|
+
rootDir: this.appConfig.rootDir,
|
|
238
|
+
outdir,
|
|
239
|
+
externalPackages: true,
|
|
240
|
+
bypassCache: options.bypassCache,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
appLogger.error(`Failed to prewarm ${options.scope} ${modulePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
263
247
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Triggers router refresh for page directory changes.
|
|
287
|
-
* This ensures the router is updated when pages are added or removed.
|
|
288
|
-
*
|
|
289
|
-
* @param {string} path - Path of the changed directory
|
|
290
|
-
*/
|
|
291
|
-
async triggerRouterRefresh(changedPath) {
|
|
292
|
-
const resolvedPath = path.resolve(changedPath);
|
|
293
|
-
const isPageDir = resolvedPath.startsWith(this.appConfig.absolutePaths.pagesDir) && path.extname(resolvedPath) === "";
|
|
294
|
-
if (isPageDir || this.isRouteSourceFile(resolvedPath)) {
|
|
295
|
-
await this.refreshRouterRoutesCallback();
|
|
248
|
+
/**
|
|
249
|
+
* Notifies all processors whose watch config matches the given file extension.
|
|
250
|
+
* This is called before checking processor ownership so that dependency-only
|
|
251
|
+
* processors (e.g. PostCSS watching .tsx for class scanning) receive their
|
|
252
|
+
* notifications regardless of whether they own the file.
|
|
253
|
+
*/
|
|
254
|
+
async notifyProcessors(filePath, event) {
|
|
255
|
+
const ctx = { path: filePath, bridge: this.bridge };
|
|
256
|
+
for (const processor of this.appConfig.processors.values()) {
|
|
257
|
+
const watchConfig = processor.getWatchConfig();
|
|
258
|
+
if (!watchConfig)
|
|
259
|
+
continue;
|
|
260
|
+
const { extensions = [] } = watchConfig;
|
|
261
|
+
if (extensions.length && !extensions.some((ext) => filePath.endsWith(ext))) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const handler = this.getProcessorHandler(watchConfig, event);
|
|
265
|
+
if (handler) {
|
|
266
|
+
await handler(ctx);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
296
269
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
270
|
+
getProcessorHandler(watchConfig, event) {
|
|
271
|
+
switch (event) {
|
|
272
|
+
case 'change':
|
|
273
|
+
return watchConfig.onChange;
|
|
274
|
+
case 'add':
|
|
275
|
+
return watchConfig.onCreate;
|
|
276
|
+
case 'unlink':
|
|
277
|
+
return watchConfig.onDelete;
|
|
278
|
+
}
|
|
306
279
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
* 1. Page file watching
|
|
313
|
-
* 2. Directory watching
|
|
314
|
-
* 3. Error handling
|
|
315
|
-
*
|
|
316
|
-
* Processor notifications are dispatched inside handleFileChange, ensuring
|
|
317
|
-
* a single unified event pipeline with no parallel chokidar bindings.
|
|
318
|
-
*
|
|
319
|
-
* Uses chokidar's built-in debouncing through `awaitWriteFinish` to handle
|
|
320
|
-
* rapid file changes efficiently.
|
|
321
|
-
*/
|
|
322
|
-
async createWatcherSubscription() {
|
|
323
|
-
if (this.watcher) {
|
|
324
|
-
return this.watcher;
|
|
280
|
+
/**
|
|
281
|
+
* Checks if a file is in the public directory.
|
|
282
|
+
*/
|
|
283
|
+
isPublicDirFile(filePath) {
|
|
284
|
+
return this.invalidationService.isPublicDirFile(filePath);
|
|
325
285
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
processorPaths.add(watchPath);
|
|
332
|
-
}
|
|
286
|
+
/**
|
|
287
|
+
* Checks if file path matches any additionalWatchPaths patterns.
|
|
288
|
+
*/
|
|
289
|
+
matchesAdditionalWatchPaths(filePath) {
|
|
290
|
+
return this.invalidationService.matchesAdditionalWatchPaths(filePath);
|
|
333
291
|
}
|
|
334
|
-
|
|
335
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Checks if a file is owned by a processor as an asset input.
|
|
294
|
+
* Ownership requires declared asset capabilities; watch config only drives
|
|
295
|
+
* {@link notifyProcessors} notifications.
|
|
296
|
+
*/
|
|
297
|
+
isHandledByProcessor(filePath) {
|
|
298
|
+
return this.invalidationService.isProcessorOwnedAsset(filePath);
|
|
336
299
|
}
|
|
337
|
-
|
|
338
|
-
|
|
300
|
+
/**
|
|
301
|
+
* Triggers router refresh for page directory changes.
|
|
302
|
+
* This ensures the router is updated when pages are added or removed.
|
|
303
|
+
*
|
|
304
|
+
* @param {string} path - Path of the changed directory
|
|
305
|
+
*/
|
|
306
|
+
async triggerRouterRefresh(changedPath) {
|
|
307
|
+
const resolvedPath = path.resolve(changedPath);
|
|
308
|
+
const isPageDir = resolvedPath.startsWith(this.appConfig.absolutePaths.pagesDir) && path.extname(resolvedPath) === '';
|
|
309
|
+
if (isPageDir || this.isRouteSourceFile(resolvedPath)) {
|
|
310
|
+
await this.refreshRouterRoutesCallback();
|
|
311
|
+
}
|
|
339
312
|
}
|
|
340
|
-
|
|
341
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Handles and logs errors that occur during file watching.
|
|
315
|
+
*
|
|
316
|
+
* @param {unknown} error - The error to handle
|
|
317
|
+
*/
|
|
318
|
+
handleError(error) {
|
|
319
|
+
if (error instanceof Error) {
|
|
320
|
+
this.hmrManager.broadcast({ type: 'error', message: error.message });
|
|
321
|
+
}
|
|
322
|
+
appLogger.error(`Watcher error: ${error}`);
|
|
342
323
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
324
|
+
/**
|
|
325
|
+
* Creates and configures the file system watcher.
|
|
326
|
+
* This sets up:
|
|
327
|
+
* 1. Page file watching
|
|
328
|
+
* 2. Directory watching
|
|
329
|
+
* 3. Error handling
|
|
330
|
+
*
|
|
331
|
+
* Processor notifications are dispatched inside handleFileChange, ensuring
|
|
332
|
+
* a single unified event pipeline with no parallel chokidar bindings.
|
|
333
|
+
*
|
|
334
|
+
* Uses chokidar's built-in debouncing through `awaitWriteFinish` to handle
|
|
335
|
+
* rapid file changes efficiently.
|
|
336
|
+
*/
|
|
337
|
+
async createWatcherSubscription() {
|
|
338
|
+
if (this.watcher) {
|
|
339
|
+
return this.watcher;
|
|
340
|
+
}
|
|
341
|
+
const processorPaths = new Set();
|
|
342
|
+
for (const processor of this.appConfig.processors.values()) {
|
|
343
|
+
const watchConfig = processor.getWatchConfig();
|
|
344
|
+
if (!watchConfig)
|
|
345
|
+
continue;
|
|
346
|
+
for (const watchPath of watchConfig.paths) {
|
|
347
|
+
processorPaths.add(watchPath);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (fileSystem.exists(this.appConfig.absolutePaths.includesDir)) {
|
|
351
|
+
processorPaths.add(this.appConfig.absolutePaths.includesDir);
|
|
352
|
+
}
|
|
353
|
+
if (fileSystem.exists(this.appConfig.absolutePaths.srcDir)) {
|
|
354
|
+
processorPaths.add(this.appConfig.absolutePaths.srcDir);
|
|
355
|
+
}
|
|
356
|
+
if (fileSystem.exists(this.appConfig.absolutePaths.publicDir)) {
|
|
357
|
+
processorPaths.add(this.appConfig.absolutePaths.publicDir);
|
|
358
|
+
}
|
|
359
|
+
for (const watchPath of this.appConfig.additionalWatchPaths) {
|
|
360
|
+
processorPaths.add(watchPath);
|
|
361
|
+
}
|
|
362
|
+
const ignored = createProjectWatcherIgnorePredicate(this.appConfig.absolutePaths);
|
|
363
|
+
this.watcher = chokidar.watch(Array.from(processorPaths), {
|
|
364
|
+
ignoreInitial: true,
|
|
365
|
+
ignorePermissionErrors: true,
|
|
366
|
+
ignored,
|
|
367
|
+
awaitWriteFinish: {
|
|
368
|
+
stabilityThreshold: 50,
|
|
369
|
+
pollInterval: 50,
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
this.watcher
|
|
373
|
+
.on('change', (p) => this.handleFileChange(p, 'change'))
|
|
374
|
+
.on('add', (p) => this.handleFileChange(p, 'add'))
|
|
375
|
+
.on('addDir', (p) => this.enqueueChange(() => this.triggerRouterRefresh(p)))
|
|
376
|
+
.on('unlink', (p) => this.handleFileChange(p, 'unlink'))
|
|
377
|
+
.on('unlinkDir', (p) => this.enqueueChange(() => this.triggerRouterRefresh(p)))
|
|
378
|
+
.on('error', (error) => this.handleError(error));
|
|
379
|
+
for (const processor of this.appConfig.processors.values()) {
|
|
380
|
+
const watchConfig = processor.getWatchConfig();
|
|
381
|
+
if (watchConfig?.onError) {
|
|
382
|
+
this.watcher.on('error', watchConfig.onError);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return this.watcher;
|
|
376
386
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
387
|
+
/**
|
|
388
|
+
* Closes the active filesystem watcher subscription.
|
|
389
|
+
*
|
|
390
|
+
* @remarks
|
|
391
|
+
* Safe to call multiple times. Used when tearing down dev servers in tests
|
|
392
|
+
* and other short-lived Ecopages runtimes.
|
|
393
|
+
*/
|
|
394
|
+
async close() {
|
|
395
|
+
this.closed = true;
|
|
396
|
+
for (const pending of this.pendingChangeEvents.values()) {
|
|
397
|
+
clearTimeout(pending.timer);
|
|
398
|
+
}
|
|
399
|
+
this.pendingChangeEvents.clear();
|
|
400
|
+
if (this.watcher) {
|
|
401
|
+
await this.watcher.close();
|
|
402
|
+
this.watcher = null;
|
|
403
|
+
}
|
|
404
|
+
await this.changeQueue.catch(() => undefined);
|
|
381
405
|
}
|
|
382
|
-
await this.changeQueue.catch(() => void 0);
|
|
383
|
-
}
|
|
384
406
|
}
|
|
385
|
-
export {
|
|
386
|
-
ProjectWatcher
|
|
387
|
-
};
|