@nativescript/vite 8.0.0-alpha.4 → 8.0.0-alpha.41
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 +51 -11
- package/bin/cli.cjs +8 -14
- package/configuration/angular.d.ts +34 -1
- package/configuration/angular.js +376 -165
- package/configuration/angular.js.map +1 -1
- package/configuration/base.js +256 -30
- package/configuration/base.js.map +1 -1
- package/configuration/javascript.js +12 -90
- package/configuration/javascript.js.map +1 -1
- package/configuration/solid.js +33 -4
- package/configuration/solid.js.map +1 -1
- package/configuration/typescript.js +10 -90
- package/configuration/typescript.js.map +1 -1
- package/helpers/app-components.d.ts +2 -1
- package/helpers/app-components.js.map +1 -1
- package/helpers/app-css-state.d.ts +8 -0
- package/helpers/app-css-state.js +8 -0
- package/helpers/app-css-state.js.map +1 -0
- package/helpers/bundler-context.d.ts +11 -0
- package/helpers/bundler-context.js +71 -0
- package/helpers/bundler-context.js.map +1 -0
- package/helpers/config-as-json.js +10 -0
- package/helpers/config-as-json.js.map +1 -1
- package/helpers/css-platform-plugin.d.ts +14 -0
- package/helpers/css-platform-plugin.js +43 -25
- package/helpers/css-platform-plugin.js.map +1 -1
- package/helpers/dev-host.d.ts +360 -0
- package/helpers/dev-host.js +692 -0
- package/helpers/dev-host.js.map +1 -0
- package/helpers/dynamic-import-plugin.js +1 -1
- package/helpers/dynamic-import-plugin.js.map +1 -1
- package/helpers/esbuild-platform-resolver.js +4 -1
- package/helpers/esbuild-platform-resolver.js.map +1 -1
- package/helpers/external-configs.d.ts +10 -12
- package/helpers/external-configs.js +54 -35
- package/helpers/external-configs.js.map +1 -1
- package/helpers/global-defines.d.ts +128 -0
- package/helpers/global-defines.js +174 -11
- package/helpers/global-defines.js.map +1 -1
- package/helpers/hmr-scope.d.ts +26 -0
- package/helpers/hmr-scope.js +67 -0
- package/helpers/hmr-scope.js.map +1 -0
- package/helpers/init.js +0 -18
- package/helpers/init.js.map +1 -1
- package/helpers/logging.d.ts +1 -0
- package/helpers/logging.js +65 -4
- package/helpers/logging.js.map +1 -1
- package/helpers/main-entry.d.ts +3 -1
- package/helpers/main-entry.js +444 -50
- package/helpers/main-entry.js.map +1 -1
- package/helpers/nativeclass-esbuild-plugin.d.ts +2 -1
- package/helpers/nativeclass-esbuild-plugin.js.map +1 -1
- package/helpers/nativeclass-transform.js +5 -6
- package/helpers/nativeclass-transform.js.map +1 -1
- package/helpers/nativeclass-transformer-plugin.d.ts +9 -2
- package/helpers/nativeclass-transformer-plugin.js +157 -14
- package/helpers/nativeclass-transformer-plugin.js.map +1 -1
- package/helpers/nativescript-package-resolver.js +10 -62
- package/helpers/nativescript-package-resolver.js.map +1 -1
- package/helpers/normalize-id.d.ts +42 -0
- package/helpers/normalize-id.js +60 -0
- package/helpers/normalize-id.js.map +1 -0
- package/helpers/ns-core-url.d.ts +106 -0
- package/helpers/ns-core-url.js +225 -0
- package/helpers/ns-core-url.js.map +1 -0
- package/helpers/optimize-deps.d.ts +16 -0
- package/helpers/optimize-deps.js +17 -0
- package/helpers/optimize-deps.js.map +1 -0
- package/helpers/package-platform-aliases.js +34 -49
- package/helpers/package-platform-aliases.js.map +1 -1
- package/helpers/platform-types.d.ts +2 -0
- package/helpers/platform-types.js +2 -0
- package/helpers/platform-types.js.map +1 -0
- package/helpers/postcss-platform-config.d.ts +17 -1
- package/helpers/postcss-platform-config.js +20 -37
- package/helpers/postcss-platform-config.js.map +1 -1
- package/helpers/project.d.ts +35 -0
- package/helpers/project.js +120 -2
- package/helpers/project.js.map +1 -1
- package/helpers/resolve-main-field-platform.d.ts +20 -0
- package/helpers/resolve-main-field-platform.js +49 -0
- package/helpers/resolve-main-field-platform.js.map +1 -0
- package/helpers/resolver.js +17 -2
- package/helpers/resolver.js.map +1 -1
- package/helpers/theme-core-plugins.js +1 -1
- package/helpers/theme-core-plugins.js.map +1 -1
- package/helpers/ts-config-paths.d.ts +14 -0
- package/helpers/ts-config-paths.js +90 -9
- package/helpers/ts-config-paths.js.map +1 -1
- package/helpers/typescript-check.d.ts +2 -1
- package/helpers/typescript-check.js.map +1 -1
- package/helpers/ui-registration.d.ts +21 -0
- package/helpers/ui-registration.js +156 -0
- package/helpers/ui-registration.js.map +1 -0
- package/helpers/utils.js +1 -2
- package/helpers/utils.js.map +1 -1
- package/helpers/workers.d.ts +20 -19
- package/helpers/workers.js +624 -4
- package/helpers/workers.js.map +1 -1
- package/hmr/client/css-handler.d.ts +2 -1
- package/hmr/client/css-handler.js +50 -26
- package/hmr/client/css-handler.js.map +1 -1
- package/hmr/client/css-update-overlay.d.ts +18 -0
- package/hmr/client/css-update-overlay.js +27 -0
- package/hmr/client/css-update-overlay.js.map +1 -0
- package/hmr/client/framework-client-strategy.d.ts +79 -0
- package/hmr/client/framework-client-strategy.js +19 -0
- package/hmr/client/framework-client-strategy.js.map +1 -0
- package/hmr/client/hmr-pending-overlay.d.ts +13 -0
- package/hmr/client/hmr-pending-overlay.js +60 -0
- package/hmr/client/hmr-pending-overlay.js.map +1 -0
- package/hmr/client/index.js +820 -212
- package/hmr/client/index.js.map +1 -1
- package/hmr/client/utils.d.ts +7 -1
- package/hmr/client/utils.js +207 -29
- package/hmr/client/utils.js.map +1 -1
- package/hmr/entry-runtime.d.ts +2 -1
- package/hmr/entry-runtime.js +256 -69
- package/hmr/entry-runtime.js.map +1 -1
- package/hmr/frameworks/angular/build/angular-linker.d.ts +12 -0
- package/hmr/frameworks/angular/build/angular-linker.js +109 -0
- package/hmr/frameworks/angular/build/angular-linker.js.map +1 -0
- package/hmr/frameworks/angular/build/inject-component-hmr-registration.d.ts +112 -0
- package/hmr/frameworks/angular/build/inject-component-hmr-registration.js +291 -0
- package/hmr/frameworks/angular/build/inject-component-hmr-registration.js.map +1 -0
- package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.d.ts +75 -0
- package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js +221 -0
- package/hmr/frameworks/angular/build/inject-hmr-vite-ignore.js.map +1 -0
- package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.js +1 -170
- package/hmr/frameworks/angular/build/inline-decorator-component-templates.js.map +1 -0
- package/hmr/frameworks/angular/build/js-lexer.d.ts +4 -0
- package/{helpers/angular/synthesize-decorator-ctor-parameters.js → hmr/frameworks/angular/build/js-lexer.js} +22 -96
- package/hmr/frameworks/angular/build/js-lexer.js.map +1 -0
- package/hmr/frameworks/angular/build/shared-linker.d.ts +39 -0
- package/hmr/frameworks/angular/build/shared-linker.js +128 -0
- package/hmr/frameworks/angular/build/shared-linker.js.map +1 -0
- package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js +88 -0
- package/hmr/frameworks/angular/build/synthesize-decorator-ctor-parameters.js.map +1 -0
- package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.js +1 -174
- package/hmr/frameworks/angular/build/synthesize-injectable-factories.js.map +1 -0
- package/{helpers/angular → hmr/frameworks/angular/build}/util.d.ts +1 -0
- package/hmr/frameworks/angular/build/util.js +155 -0
- package/hmr/frameworks/angular/build/util.js.map +1 -0
- package/hmr/frameworks/angular/client/index.d.ts +1 -0
- package/hmr/frameworks/angular/client/index.js +803 -21
- package/hmr/frameworks/angular/client/index.js.map +1 -1
- package/hmr/frameworks/angular/client/strategy.d.ts +9 -0
- package/hmr/frameworks/angular/client/strategy.js +19 -0
- package/hmr/frameworks/angular/client/strategy.js.map +1 -0
- package/hmr/frameworks/angular/server/angular-root-component.d.ts +79 -0
- package/hmr/frameworks/angular/server/angular-root-component.js +149 -0
- package/hmr/frameworks/angular/server/angular-root-component.js.map +1 -0
- package/hmr/frameworks/angular/server/linker.js +1 -4
- package/hmr/frameworks/angular/server/linker.js.map +1 -1
- package/hmr/frameworks/angular/server/strategy.js +460 -12
- package/hmr/frameworks/angular/server/strategy.js.map +1 -1
- package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.js +2 -2
- package/hmr/frameworks/angular/server/websocket-angular-entry.js.map +1 -0
- package/hmr/{server → frameworks/angular/server}/websocket-angular-hot-update.d.ts +17 -11
- package/hmr/frameworks/angular/server/websocket-angular-hot-update.js +336 -0
- package/hmr/frameworks/angular/server/websocket-angular-hot-update.js.map +1 -0
- package/hmr/frameworks/solid/build/solid-jsx-deps.d.ts +15 -0
- package/hmr/frameworks/solid/build/solid-jsx-deps.js +178 -0
- package/hmr/frameworks/solid/build/solid-jsx-deps.js.map +1 -0
- package/hmr/frameworks/solid/server/strategy.js +291 -16
- package/hmr/frameworks/solid/server/strategy.js.map +1 -1
- package/hmr/frameworks/typescript/server/strategy.js +38 -14
- package/hmr/frameworks/typescript/server/strategy.js.map +1 -1
- package/hmr/frameworks/vue/client/dep-propagation.d.ts +36 -0
- package/hmr/frameworks/vue/client/dep-propagation.js +101 -0
- package/hmr/frameworks/vue/client/dep-propagation.js.map +1 -0
- package/hmr/frameworks/vue/client/index.d.ts +8 -0
- package/hmr/frameworks/vue/client/index.js +56 -243
- package/hmr/frameworks/vue/client/index.js.map +1 -1
- package/hmr/frameworks/vue/client/strategy.d.ts +33 -0
- package/hmr/frameworks/vue/client/strategy.js +157 -0
- package/hmr/frameworks/vue/client/strategy.js.map +1 -0
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.d.ts +49 -0
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js +142 -0
- package/hmr/frameworks/vue/client/vue-sfc-update-overlay.js.map +1 -0
- package/hmr/frameworks/vue/server/sfc-route-assemble.d.ts +7 -0
- package/hmr/frameworks/vue/server/sfc-route-assemble.js +747 -0
- package/hmr/frameworks/vue/server/sfc-route-assemble.js.map +1 -0
- package/hmr/frameworks/vue/server/sfc-route-meta.d.ts +7 -0
- package/hmr/frameworks/vue/server/sfc-route-meta.js +80 -0
- package/hmr/frameworks/vue/server/sfc-route-meta.js.map +1 -0
- package/hmr/frameworks/vue/server/sfc-route-serve.d.ts +8 -0
- package/hmr/frameworks/vue/server/sfc-route-serve.js +459 -0
- package/hmr/frameworks/vue/server/sfc-route-serve.js.map +1 -0
- package/hmr/frameworks/vue/server/sfc-route-shared.d.ts +38 -0
- package/hmr/frameworks/vue/server/sfc-route-shared.js +48 -0
- package/hmr/frameworks/vue/server/sfc-route-shared.js.map +1 -0
- package/hmr/frameworks/vue/server/strategy.d.ts +35 -0
- package/hmr/frameworks/vue/server/strategy.js +278 -1
- package/hmr/frameworks/vue/server/strategy.js.map +1 -1
- package/hmr/frameworks/vue/server/websocket-sfc.d.ts +15 -0
- package/hmr/frameworks/vue/server/websocket-sfc.js +20 -0
- package/hmr/frameworks/vue/server/websocket-sfc.js.map +1 -0
- package/hmr/helpers/ast-normalizer.d.ts +3 -1
- package/hmr/helpers/ast-normalizer.js +77 -10
- package/hmr/helpers/ast-normalizer.js.map +1 -1
- package/hmr/helpers/cjs-named-exports.d.ts +23 -0
- package/hmr/helpers/cjs-named-exports.js +152 -0
- package/hmr/helpers/cjs-named-exports.js.map +1 -0
- package/hmr/helpers/package-exports.d.ts +16 -0
- package/hmr/helpers/package-exports.js +396 -0
- package/hmr/helpers/package-exports.js.map +1 -0
- package/hmr/server/constants.js +20 -5
- package/hmr/server/constants.js.map +1 -1
- package/hmr/server/core-sanitize.d.ts +90 -7
- package/hmr/server/core-sanitize.js +211 -56
- package/hmr/server/core-sanitize.js.map +1 -1
- package/hmr/server/device-transform-helpers.d.ts +24 -0
- package/hmr/server/device-transform-helpers.js +369 -0
- package/hmr/server/device-transform-helpers.js.map +1 -0
- package/hmr/server/framework-strategy.d.ts +108 -11
- package/hmr/server/hmr-module-graph.d.ts +37 -0
- package/hmr/server/hmr-module-graph.js +214 -0
- package/hmr/server/hmr-module-graph.js.map +1 -0
- package/hmr/server/import-map.d.ts +11 -2
- package/hmr/server/import-map.js +95 -44
- package/hmr/server/import-map.js.map +1 -1
- package/hmr/server/index.js +7 -16
- package/hmr/server/index.js.map +1 -1
- package/hmr/server/ns-core-cjs-shape.d.ts +204 -0
- package/hmr/server/ns-core-cjs-shape.js +271 -0
- package/hmr/server/ns-core-cjs-shape.js.map +1 -0
- package/hmr/server/ns-rt-bridge.d.ts +51 -0
- package/hmr/server/ns-rt-bridge.js +131 -0
- package/hmr/server/ns-rt-bridge.js.map +1 -0
- package/hmr/server/ns-rt-route.d.ts +5 -0
- package/hmr/server/ns-rt-route.js +38 -0
- package/hmr/server/ns-rt-route.js.map +1 -0
- package/hmr/server/perf-instrumentation.d.ts +114 -0
- package/hmr/server/perf-instrumentation.js +197 -0
- package/hmr/server/perf-instrumentation.js.map +1 -0
- package/hmr/server/process-code-for-device.d.ts +14 -0
- package/hmr/server/process-code-for-device.js +702 -0
- package/hmr/server/process-code-for-device.js.map +1 -0
- package/hmr/server/require-guard.d.ts +1 -0
- package/hmr/server/require-guard.js +12 -0
- package/hmr/server/require-guard.js.map +1 -0
- package/hmr/server/rewrite-imports.d.ts +2 -0
- package/hmr/server/rewrite-imports.js +600 -0
- package/hmr/server/rewrite-imports.js.map +1 -0
- package/hmr/server/route-helpers.d.ts +7 -0
- package/hmr/server/route-helpers.js +13 -0
- package/hmr/server/route-helpers.js.map +1 -0
- package/hmr/server/server-origin.d.ts +2 -0
- package/hmr/server/server-origin.js +83 -0
- package/hmr/server/server-origin.js.map +1 -0
- package/hmr/server/shared-transform-request.js +13 -7
- package/hmr/server/shared-transform-request.js.map +1 -1
- package/hmr/server/transform-cache-invalidation.d.ts +37 -0
- package/hmr/server/transform-cache-invalidation.js +156 -0
- package/hmr/server/transform-cache-invalidation.js.map +1 -0
- package/hmr/server/vendor-bare-module-shims.d.ts +4 -0
- package/hmr/server/vendor-bare-module-shims.js +80 -0
- package/hmr/server/vendor-bare-module-shims.js.map +1 -0
- package/hmr/server/vite-plugin.js +72 -42
- package/hmr/server/vite-plugin.js.map +1 -1
- package/hmr/server/websocket-core-bridge.d.ts +45 -6
- package/hmr/server/websocket-core-bridge.js +81 -77
- package/hmr/server/websocket-core-bridge.js.map +1 -1
- package/hmr/server/websocket-css-hot-update.d.ts +33 -0
- package/hmr/server/websocket-css-hot-update.js +65 -0
- package/hmr/server/websocket-css-hot-update.js.map +1 -0
- package/hmr/server/websocket-device-transform.d.ts +3 -0
- package/hmr/server/websocket-device-transform.js +7 -0
- package/hmr/server/websocket-device-transform.js.map +1 -0
- package/hmr/server/websocket-graph-upsert.d.ts +15 -0
- package/hmr/server/websocket-graph-upsert.js +20 -0
- package/hmr/server/websocket-graph-upsert.js.map +1 -1
- package/hmr/server/websocket-hmr-pending.d.ts +37 -0
- package/hmr/server/websocket-hmr-pending.js +55 -0
- package/hmr/server/websocket-hmr-pending.js.map +1 -0
- package/hmr/server/websocket-hot-update.d.ts +77 -0
- package/hmr/server/websocket-hot-update.js +360 -0
- package/hmr/server/websocket-hot-update.js.map +1 -0
- package/hmr/server/websocket-import-map-route.d.ts +15 -0
- package/hmr/server/websocket-import-map-route.js +50 -0
- package/hmr/server/websocket-import-map-route.js.map +1 -0
- package/hmr/server/websocket-module-bindings.js +3 -3
- package/hmr/server/websocket-module-bindings.js.map +1 -1
- package/hmr/server/websocket-module-specifiers.d.ts +66 -2
- package/hmr/server/websocket-module-specifiers.js +203 -20
- package/hmr/server/websocket-module-specifiers.js.map +1 -1
- package/hmr/server/websocket-ns-core.d.ts +21 -0
- package/hmr/server/websocket-ns-core.js +311 -0
- package/hmr/server/websocket-ns-core.js.map +1 -0
- package/hmr/server/websocket-ns-entry.d.ts +21 -0
- package/hmr/server/websocket-ns-entry.js +164 -0
- package/hmr/server/websocket-ns-entry.js.map +1 -0
- package/hmr/server/websocket-ns-m-paths.d.ts +1 -1
- package/hmr/server/websocket-ns-m-paths.js +58 -13
- package/hmr/server/websocket-ns-m-paths.js.map +1 -1
- package/hmr/server/websocket-ns-m-request.d.ts +11 -1
- package/hmr/server/websocket-ns-m-request.js +18 -25
- package/hmr/server/websocket-ns-m-request.js.map +1 -1
- package/hmr/server/websocket-ns-m.d.ts +33 -0
- package/hmr/server/websocket-ns-m.js +752 -0
- package/hmr/server/websocket-ns-m.js.map +1 -0
- package/hmr/server/websocket-served-module-helpers.d.ts +82 -0
- package/hmr/server/websocket-served-module-helpers.js +879 -0
- package/hmr/server/websocket-served-module-helpers.js.map +1 -0
- package/hmr/server/websocket-txn.js +2 -8
- package/hmr/server/websocket-txn.js.map +1 -1
- package/hmr/server/websocket-vendor-unifier.d.ts +0 -1
- package/hmr/server/websocket-vendor-unifier.js +4 -9
- package/hmr/server/websocket-vendor-unifier.js.map +1 -1
- package/hmr/server/websocket.d.ts +8 -39
- package/hmr/server/websocket.js +514 -4030
- package/hmr/server/websocket.js.map +1 -1
- package/hmr/shared/ns-globals.d.ts +118 -0
- package/hmr/shared/ns-globals.js +29 -0
- package/hmr/shared/ns-globals.js.map +1 -0
- package/hmr/shared/protocol.d.ts +145 -0
- package/hmr/shared/protocol.js +28 -0
- package/hmr/shared/protocol.js.map +1 -0
- package/hmr/shared/runtime/boot-placeholder-ui.d.ts +69 -0
- package/hmr/shared/runtime/boot-placeholder-ui.js +101 -0
- package/hmr/shared/runtime/boot-placeholder-ui.js.map +1 -0
- package/hmr/shared/runtime/boot-progress.d.ts +44 -0
- package/hmr/shared/runtime/boot-progress.js +133 -0
- package/hmr/shared/runtime/boot-progress.js.map +1 -0
- package/hmr/shared/runtime/boot-timeline.d.ts +18 -0
- package/hmr/shared/runtime/boot-timeline.js +42 -0
- package/hmr/shared/runtime/boot-timeline.js.map +1 -0
- package/hmr/shared/runtime/browser-runtime-contract.js.map +1 -1
- package/hmr/shared/runtime/dev-overlay-snapshots.d.ts +31 -0
- package/hmr/shared/runtime/dev-overlay-snapshots.js +324 -0
- package/hmr/shared/runtime/dev-overlay-snapshots.js.map +1 -0
- package/hmr/shared/runtime/dev-overlay.d.ts +119 -26
- package/hmr/shared/runtime/dev-overlay.js +1153 -261
- package/hmr/shared/runtime/dev-overlay.js.map +1 -1
- package/hmr/shared/runtime/global-scope.d.ts +18 -0
- package/hmr/shared/runtime/global-scope.js +21 -0
- package/hmr/shared/runtime/global-scope.js.map +1 -0
- package/hmr/shared/runtime/hooks.js +2 -1
- package/hmr/shared/runtime/hooks.js.map +1 -1
- package/hmr/shared/runtime/http-only-boot.js +7 -6
- package/hmr/shared/runtime/http-only-boot.js.map +1 -1
- package/hmr/shared/runtime/module-provenance.js +4 -6
- package/hmr/shared/runtime/module-provenance.js.map +1 -1
- package/hmr/shared/runtime/root-placeholder-view.d.ts +19 -0
- package/hmr/shared/runtime/root-placeholder-view.js +311 -0
- package/hmr/shared/runtime/root-placeholder-view.js.map +1 -0
- package/hmr/shared/runtime/root-placeholder.js +371 -197
- package/hmr/shared/runtime/root-placeholder.js.map +1 -1
- package/hmr/shared/runtime/session-bootstrap.js +168 -4
- package/hmr/shared/runtime/session-bootstrap.js.map +1 -1
- package/hmr/shared/runtime/vendor-bootstrap.js +3 -10
- package/hmr/shared/runtime/vendor-bootstrap.js.map +1 -1
- package/hmr/shared/vendor/manifest-collect.d.ts +4 -0
- package/hmr/shared/vendor/manifest-collect.js +512 -0
- package/hmr/shared/vendor/manifest-collect.js.map +1 -0
- package/hmr/shared/vendor/manifest-loader.d.ts +2 -1
- package/hmr/shared/vendor/manifest-loader.js +5 -3
- package/hmr/shared/vendor/manifest-loader.js.map +1 -1
- package/hmr/shared/vendor/manifest.d.ts +1 -7
- package/hmr/shared/vendor/manifest.js +102 -741
- package/hmr/shared/vendor/manifest.js.map +1 -1
- package/hmr/shared/vendor/vendor-device-shim.d.ts +1 -0
- package/hmr/shared/vendor/vendor-device-shim.js +208 -0
- package/hmr/shared/vendor/vendor-device-shim.js.map +1 -0
- package/hmr/shared/vendor/vendor-esbuild-plugins.d.ts +16 -0
- package/hmr/shared/vendor/vendor-esbuild-plugins.js +203 -0
- package/hmr/shared/vendor/vendor-esbuild-plugins.js.map +1 -0
- package/hmr/vendor-bootstrap.d.ts +1 -3
- package/hmr/vendor-bootstrap.js +4 -6
- package/hmr/vendor-bootstrap.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +5 -0
- package/index.js.map +1 -1
- package/package.json +55 -11
- package/runtime/core-aliases-early.js +25 -55
- package/runtime/core-aliases-early.js.map +1 -1
- package/helpers/angular/angular-linker.d.ts +0 -13
- package/helpers/angular/angular-linker.js +0 -194
- package/helpers/angular/angular-linker.js.map +0 -1
- package/helpers/angular/inline-decorator-component-templates.js.map +0 -1
- package/helpers/angular/shared-linker.d.ts +0 -11
- package/helpers/angular/shared-linker.js +0 -75
- package/helpers/angular/shared-linker.js.map +0 -1
- package/helpers/angular/synthesize-decorator-ctor-parameters.js.map +0 -1
- package/helpers/angular/synthesize-injectable-factories.js.map +0 -1
- package/helpers/angular/util.js +0 -67
- package/helpers/angular/util.js.map +0 -1
- package/helpers/prelink-angular.d.ts +0 -2
- package/helpers/prelink-angular.js +0 -117
- package/helpers/prelink-angular.js.map +0 -1
- package/hmr/server/websocket-angular-entry.js.map +0 -1
- package/hmr/server/websocket-angular-hot-update.js +0 -239
- package/hmr/server/websocket-angular-hot-update.js.map +0 -1
- package/hmr/server/websocket-ns-m-finalize.d.ts +0 -32
- package/hmr/server/websocket-ns-m-finalize.js +0 -73
- package/hmr/server/websocket-ns-m-finalize.js.map +0 -1
- package/hmr/server/websocket-runtime-compat.d.ts +0 -19
- package/hmr/server/websocket-runtime-compat.js +0 -286
- package/hmr/server/websocket-runtime-compat.js.map +0 -1
- package/hmr/server/websocket-vue-sfc.d.ts +0 -35
- package/hmr/server/websocket-vue-sfc.js +0 -1116
- package/hmr/server/websocket-vue-sfc.js.map +0 -1
- package/transformers/NativeClass/index.d.ts +0 -2
- package/transformers/NativeClass/index.js +0 -222
- package/transformers/NativeClass/index.js.map +0 -1
- /package/{helpers/angular → hmr/frameworks/angular/build}/inline-decorator-component-templates.d.ts +0 -0
- /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-decorator-ctor-parameters.d.ts +0 -0
- /package/{helpers/angular → hmr/frameworks/angular/build}/synthesize-injectable-factories.d.ts +0 -0
- /package/hmr/{server → frameworks/angular/server}/websocket-angular-entry.d.ts +0 -0
package/hmr/client/index.js
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* Always resolve core classes and Application from the vendor realm or globalThis at runtime.
|
|
6
6
|
* The HMR client is evaluated via HTTP ESM on device; static imports would create secondary instances.
|
|
7
7
|
*/
|
|
8
|
-
import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore } from './utils.js';
|
|
8
|
+
import { setHMRWsUrl, getHMRWsUrl, pendingModuleFetches, deriveHttpOrigin, setHttpOriginForVite, moduleFetchCache, requestModuleFromServer, getHttpOriginForVite, normalizeSpec, hmrMetrics, graph, setGraphVersion, getGraphVersion, getCurrentApp, getRootFrame, setCurrentApp, setRootFrame, getCore, hasExplicitEviction, invalidateModulesByUrls, buildEvictionUrls, emitHmrModeBannerOnce, ENV_VERBOSE } from './utils.js';
|
|
9
9
|
import { handleCssUpdates } from './css-handler.js';
|
|
10
|
-
|
|
10
|
+
import { buildCssApplyingDetail, buildCssAppliedDetail } from './css-update-overlay.js';
|
|
11
|
+
import { getGlobalScope } from '../shared/runtime/global-scope.js';
|
|
12
|
+
const VERBOSE = ENV_VERBOSE;
|
|
11
13
|
function resolveTargetFlavor() {
|
|
12
14
|
try {
|
|
13
15
|
if (typeof __NS_TARGET_FLAVOR__ !== 'undefined' && __NS_TARGET_FLAVOR__) {
|
|
@@ -16,7 +18,7 @@ function resolveTargetFlavor() {
|
|
|
16
18
|
}
|
|
17
19
|
catch { }
|
|
18
20
|
try {
|
|
19
|
-
const g =
|
|
21
|
+
const g = getGlobalScope();
|
|
20
22
|
if (typeof g.__NS_TARGET_FLAVOR__ === 'string' && g.__NS_TARGET_FLAVOR__) {
|
|
21
23
|
return g.__NS_TARGET_FLAVOR__;
|
|
22
24
|
}
|
|
@@ -40,7 +42,11 @@ try {
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
catch { }
|
|
43
|
-
|
|
45
|
+
// Define substitution does NOT reach this file (served raw from node_modules),
|
|
46
|
+
// so prefer the globalThis seed planted by the entry's defines-seed module —
|
|
47
|
+
// the '/src' literal is a last-resort default and is WRONG for 'app/'-rooted
|
|
48
|
+
// projects.
|
|
49
|
+
const APP_ROOT_VIRTUAL = (typeof __NS_APP_ROOT_VIRTUAL__ === 'string' && __NS_APP_ROOT_VIRTUAL__) || (typeof getGlobalScope().__NS_APP_ROOT_VIRTUAL__ === 'string' && getGlobalScope().__NS_APP_ROOT_VIRTUAL__) || '/src';
|
|
44
50
|
const APP_VIRTUAL_WITH_SLASH = APP_ROOT_VIRTUAL.endsWith('/') ? APP_ROOT_VIRTUAL : `${APP_ROOT_VIRTUAL}/`;
|
|
45
51
|
const APP_MAIN_ENTRY_SPEC = `${APP_VIRTUAL_WITH_SLASH}app.ts`;
|
|
46
52
|
// Policy: by default, let the app's own main entry mount initially; HMR client handles updates/remounts only.
|
|
@@ -101,12 +107,101 @@ function hideConnectionOverlay() {
|
|
|
101
107
|
}
|
|
102
108
|
catch { }
|
|
103
109
|
}
|
|
110
|
+
function setUpdateOverlayStage(stage, info) {
|
|
111
|
+
try {
|
|
112
|
+
const api = getHmrOverlayApi();
|
|
113
|
+
if (api && typeof api.setUpdateStage === 'function') {
|
|
114
|
+
api.setUpdateStage(stage, info);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
}
|
|
119
|
+
// Store the listener registry on globalThis (rather than in a module-private
|
|
120
|
+
// closure) because in NativeScript the HMR client module and the user app
|
|
121
|
+
// modules can resolve to different module instances depending on how the
|
|
122
|
+
// dev runtime loads them (HTTP client URL vs. the bundled vendor realm).
|
|
123
|
+
// A module-local Set would not be shared across instances; the global one
|
|
124
|
+
// is.
|
|
125
|
+
function getNsSolidHmrListenerSet() {
|
|
126
|
+
const g = getGlobalScope();
|
|
127
|
+
let set = g.__ns_solid_hmr_listener_set;
|
|
128
|
+
if (!set) {
|
|
129
|
+
set = new Set();
|
|
130
|
+
g.__ns_solid_hmr_listener_set = set;
|
|
131
|
+
}
|
|
132
|
+
return set;
|
|
133
|
+
}
|
|
134
|
+
function nsSolidHmrSubscribe(fn) {
|
|
135
|
+
const listeners = getNsSolidHmrListenerSet();
|
|
136
|
+
listeners.add(fn);
|
|
137
|
+
if (VERBOSE)
|
|
138
|
+
console.log('[hmr][solid] subscribe — listeners=', listeners.size);
|
|
139
|
+
return () => listeners.delete(fn);
|
|
140
|
+
}
|
|
141
|
+
function nsSolidHmrEmit(ev) {
|
|
142
|
+
const listeners = getNsSolidHmrListenerSet();
|
|
143
|
+
if (VERBOSE)
|
|
144
|
+
console.log('[hmr][solid] emit listeners=', listeners.size, 'changedFiles=', ev.changedFiles);
|
|
145
|
+
for (const fn of Array.from(listeners)) {
|
|
146
|
+
try {
|
|
147
|
+
fn(ev);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
if (VERBOSE)
|
|
151
|
+
console.warn('[hmr][solid] listener threw', err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const g = getGlobalScope();
|
|
157
|
+
g.__ns_solid_hmr_subscribe = nsSolidHmrSubscribe;
|
|
158
|
+
// Eagerly create the listener set so the global exists at module load time.
|
|
159
|
+
getNsSolidHmrListenerSet();
|
|
160
|
+
if (VERBOSE)
|
|
161
|
+
console.log('[hmr][solid] HMR client loaded. global set=', typeof g.__ns_solid_hmr_subscribe, 'listenerSet=', typeof g.__ns_solid_hmr_listener_set);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
console.warn('[hmr][solid] could not install global __ns_solid_hmr_subscribe', err);
|
|
165
|
+
}
|
|
166
|
+
// Eagerly drive the HMR-applying overlay's 'received' frame as soon
|
|
167
|
+
// as the server emits `ns:hmr-pending`, BEFORE the framework-specific
|
|
168
|
+
// (`ns:angular-update` / `ns:css-updates`) payload arrives. The
|
|
169
|
+
// flavor-specific handler later walks through 'evicting' →
|
|
170
|
+
// 'reimporting' → 'rebooting' → 'complete'. Calling 'received' twice
|
|
171
|
+
// in the same cycle is safe: the overlay preserves
|
|
172
|
+
// `updateCycleStartedAt` when a 'received' frame replaces an existing
|
|
173
|
+
// 'received' frame so the minimum-visible window is still timed
|
|
174
|
+
// against the FIRST frame.
|
|
175
|
+
//
|
|
176
|
+
// Soft-fails when the overlay isn't installed (production builds,
|
|
177
|
+
// vitest, etc.) or when the user opted out via
|
|
178
|
+
// `__NS_HMR_PROGRESS_OVERLAY_ENABLED__ === false`.
|
|
179
|
+
import { applyHmrPendingFrame } from './hmr-pending-overlay.js';
|
|
180
|
+
function setHmrPendingOverlay(filePath) {
|
|
181
|
+
applyHmrPendingFrame(filePath, { getOverlay: getHmrOverlayApi });
|
|
182
|
+
}
|
|
104
183
|
let connectionOverlayTimer = null;
|
|
105
184
|
let connectionOverlayVisible = false;
|
|
106
185
|
let hasOpenedHmrSocket = false;
|
|
107
186
|
let awaitingHealthyHmrMessage = false;
|
|
108
187
|
let pendingConnectionOverlayStage = 'connecting';
|
|
109
188
|
let pendingConnectionOverlayDetail = '';
|
|
189
|
+
// The stage currently PAINTED on screen (set only when we actually show
|
|
190
|
+
// the overlay, not when we merely schedule it). Used to suppress the
|
|
191
|
+
// sub-perceptible offline↔reconnecting flicker during the reconnect
|
|
192
|
+
// retry loop — see the deferral in `connectHmr`/`tryNext`.
|
|
193
|
+
let shownConnectionOverlayStage = null;
|
|
194
|
+
// While the terminal 'offline' frame is showing, a fresh reconnect
|
|
195
|
+
// attempt that is refused near-instantly (the norm when the dev server
|
|
196
|
+
// is down — especially on the Android emulator, where connection-refused
|
|
197
|
+
// returns in ~1ms) would flip the overlay to 'reconnecting' and straight
|
|
198
|
+
// back to 'offline', producing a jarring 1-frame flicker. We defer the
|
|
199
|
+
// 'reconnecting' frame by this much so an instant failure (→ back to
|
|
200
|
+
// 'offline') or a success (→ 'synchronizing') cancels it before it ever
|
|
201
|
+
// paints; only a genuinely in-flight attempt surfaces 'reconnecting'.
|
|
202
|
+
// On iOS real connect attempts exceed this threshold, so the behaviour
|
|
203
|
+
// there is unchanged.
|
|
204
|
+
const RECONNECTING_OVER_OFFLINE_DELAY_MS = 500;
|
|
110
205
|
function clearConnectionOverlayTimer() {
|
|
111
206
|
if (connectionOverlayTimer) {
|
|
112
207
|
clearTimeout(connectionOverlayTimer);
|
|
@@ -114,9 +209,14 @@ function clearConnectionOverlayTimer() {
|
|
|
114
209
|
}
|
|
115
210
|
}
|
|
116
211
|
function showConnectionOverlayNow(stage, detail) {
|
|
212
|
+
// Painting a stage now supersedes any scheduled (deferred) stage, so
|
|
213
|
+
// cancel the pending timer — otherwise a deferred 'reconnecting' could
|
|
214
|
+
// fire moments after we settle back on 'offline' and revive the flicker.
|
|
215
|
+
clearConnectionOverlayTimer();
|
|
117
216
|
pendingConnectionOverlayStage = stage;
|
|
118
217
|
pendingConnectionOverlayDetail = detail || '';
|
|
119
218
|
connectionOverlayVisible = true;
|
|
219
|
+
shownConnectionOverlayStage = stage;
|
|
120
220
|
setConnectionOverlayStage(stage, detail);
|
|
121
221
|
}
|
|
122
222
|
function scheduleConnectionOverlay(stage, detail, delayMs = 1200) {
|
|
@@ -137,24 +237,25 @@ function updateConnectionOverlay(stage, detail) {
|
|
|
137
237
|
function markHmrConnectionHealthy() {
|
|
138
238
|
awaitingHealthyHmrMessage = false;
|
|
139
239
|
clearConnectionOverlayTimer();
|
|
240
|
+
shownConnectionOverlayStage = null;
|
|
140
241
|
if (connectionOverlayVisible) {
|
|
141
242
|
connectionOverlayVisible = false;
|
|
142
243
|
hideConnectionOverlay();
|
|
143
244
|
}
|
|
144
245
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
246
|
+
let CLIENT_STRATEGY;
|
|
247
|
+
const CLIENT_STRATEGY_READY = TARGET_FLAVOR === 'vue' || TARGET_FLAVOR === 'angular'
|
|
248
|
+
? import(`../frameworks/${TARGET_FLAVOR}/client/strategy.js`)
|
|
249
|
+
.then((mod) => {
|
|
250
|
+
CLIENT_STRATEGY = mod && mod[`${TARGET_FLAVOR}ClientStrategy`];
|
|
251
|
+
if (VERBOSE)
|
|
252
|
+
console.log('[hmr-client] client strategy loaded for flavor:', TARGET_FLAVOR);
|
|
253
|
+
CLIENT_STRATEGY?.install();
|
|
254
|
+
})
|
|
255
|
+
.catch((err) => {
|
|
256
|
+
console.warn('[hmr-client] failed to load client strategy for', TARGET_FLAVOR, err);
|
|
257
|
+
})
|
|
258
|
+
: Promise.resolve();
|
|
158
259
|
// Track whether we've mounted an initial app root yet in HTTP-only boot
|
|
159
260
|
let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
160
261
|
// Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
|
|
@@ -172,7 +273,7 @@ let processingPromise = null;
|
|
|
172
273
|
// Detect whether the early placeholder root is still active on screen
|
|
173
274
|
function isPlaceholderActive() {
|
|
174
275
|
try {
|
|
175
|
-
const g =
|
|
276
|
+
const g = getGlobalScope();
|
|
176
277
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__)
|
|
177
278
|
return true;
|
|
178
279
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__)
|
|
@@ -202,7 +303,7 @@ function applyFullGraph(payload) {
|
|
|
202
303
|
// causes a double-mount race (rescue fires at 450ms, then main.ts fires ~1s later,
|
|
203
304
|
// causing a visual flash and leaving the app in an inconsistent state).
|
|
204
305
|
try {
|
|
205
|
-
const g =
|
|
306
|
+
const g = getGlobalScope();
|
|
206
307
|
const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
207
308
|
if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__ && TARGET_FLAVOR !== 'typescript') {
|
|
208
309
|
// simple snapshot helpers
|
|
@@ -317,39 +418,7 @@ function applyFullGraph(payload) {
|
|
|
317
418
|
}
|
|
318
419
|
return;
|
|
319
420
|
}
|
|
320
|
-
|
|
321
|
-
switch (TARGET_FLAVOR) {
|
|
322
|
-
case 'vue': {
|
|
323
|
-
const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
|
|
324
|
-
if (appEntry && Array.isArray(appEntry.deps)) {
|
|
325
|
-
const vueDep = appEntry.deps.find((d) => typeof d === 'string' && /\.vue$/i.test(d));
|
|
326
|
-
if (vueDep)
|
|
327
|
-
candidate = vueDep;
|
|
328
|
-
}
|
|
329
|
-
if (!candidate) {
|
|
330
|
-
for (const id of graph.keys()) {
|
|
331
|
-
if (/\.vue$/i.test(id)) {
|
|
332
|
-
candidate = id;
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
// Fallback: when the module graph is empty (Vite 7+ may not populate it
|
|
338
|
-
// before the first full-graph broadcast), check the SFC artifact registry
|
|
339
|
-
// which is populated from the ns:vue-sfc-registry message.
|
|
340
|
-
if (!candidate && sfcArtifactMap.size > 0) {
|
|
341
|
-
for (const id of sfcArtifactMap.keys()) {
|
|
342
|
-
if (/\.vue$/i.test(id)) {
|
|
343
|
-
candidate = id;
|
|
344
|
-
if (VERBOSE)
|
|
345
|
-
console.log('[hmr][init] rescue candidate from SFC registry:', id);
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
421
|
+
const candidate = CLIENT_STRATEGY?.selectMountCandidate?.({ graph, appMainEntrySpec: APP_MAIN_ENTRY_SPEC }) ?? null;
|
|
353
422
|
if (!candidate)
|
|
354
423
|
return;
|
|
355
424
|
initialMounting = true;
|
|
@@ -360,10 +429,8 @@ function applyFullGraph(payload) {
|
|
|
360
429
|
(async () => {
|
|
361
430
|
try {
|
|
362
431
|
let comp = null;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
comp = await loadSfcComponent(candidate, 'initial_mount_rescue');
|
|
366
|
-
break;
|
|
432
|
+
if (CLIENT_STRATEGY?.loadComponentForMount) {
|
|
433
|
+
comp = await CLIENT_STRATEGY.loadComponentForMount(candidate, 'initial_mount_rescue');
|
|
367
434
|
}
|
|
368
435
|
if (!comp)
|
|
369
436
|
return;
|
|
@@ -404,41 +471,11 @@ function applyFullGraph(payload) {
|
|
|
404
471
|
// Only allow initial mount when explicitly enabled. Rely on the app's own main entry start() for the first mount
|
|
405
472
|
// to avoid double-mount races that can cause duplicate navigation logs.
|
|
406
473
|
if (ALLOW_INITIAL_MOUNT && !initialMounted && !bootDone && !bootInProgress && !getCurrentApp() && !getRootFrame()) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const appEntry = graph.get(APP_MAIN_ENTRY_SPEC);
|
|
411
|
-
if (appEntry && Array.isArray(appEntry.deps)) {
|
|
412
|
-
const vueDep = appEntry.deps.find((d) => typeof d === 'string' && /\.vue$/i.test(d));
|
|
413
|
-
if (vueDep)
|
|
414
|
-
candidate = vueDep;
|
|
415
|
-
}
|
|
416
|
-
if (!candidate) {
|
|
417
|
-
for (const id of graph.keys()) {
|
|
418
|
-
if (/\.vue$/i.test(id)) {
|
|
419
|
-
candidate = id;
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
// Fallback: SFC registry (same as rescue mount above)
|
|
425
|
-
if (!candidate && sfcArtifactMap.size > 0) {
|
|
426
|
-
for (const id of sfcArtifactMap.keys()) {
|
|
427
|
-
if (/\.vue$/i.test(id)) {
|
|
428
|
-
candidate = id;
|
|
429
|
-
if (VERBOSE)
|
|
430
|
-
console.log('[hmr][init] initial mount candidate from SFC registry:', id);
|
|
431
|
-
break;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
break;
|
|
436
|
-
}
|
|
437
|
-
case 'typescript': {
|
|
438
|
-
// For TS flavor, do not perform client-driven initial mount; rely on Application.run.
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
474
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
475
|
+
// For TS flavor, do not perform client-driven initial mount; rely on Application.run.
|
|
476
|
+
return;
|
|
441
477
|
}
|
|
478
|
+
const candidate = CLIENT_STRATEGY?.selectMountCandidate?.({ graph, appMainEntrySpec: APP_MAIN_ENTRY_SPEC }) ?? null;
|
|
442
479
|
if (candidate) {
|
|
443
480
|
// Mark initial-mount in progress (both module-local and global) BEFORE scheduling async work
|
|
444
481
|
initialMounting = true;
|
|
@@ -452,7 +489,7 @@ function applyFullGraph(payload) {
|
|
|
452
489
|
console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', TARGET_FLAVOR);
|
|
453
490
|
// Android-only: avoid racing entry-runtime reset and Activity bring-up
|
|
454
491
|
try {
|
|
455
|
-
const g =
|
|
492
|
+
const g = getGlobalScope();
|
|
456
493
|
const App = getCore('Application') || g.Application;
|
|
457
494
|
const isAndroid = !!(App && App.android !== undefined);
|
|
458
495
|
if (isAndroid) {
|
|
@@ -483,21 +520,19 @@ function applyFullGraph(payload) {
|
|
|
483
520
|
}
|
|
484
521
|
catch { }
|
|
485
522
|
let comp = null;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
break;
|
|
523
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
524
|
+
try {
|
|
525
|
+
const url = await requestModuleFromServer(candidate);
|
|
526
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
527
|
+
comp = mod && (mod.default || mod);
|
|
528
|
+
}
|
|
529
|
+
catch (e) {
|
|
530
|
+
if (VERBOSE)
|
|
531
|
+
console.warn('[hmr][init] TS initial mount failed to import', candidate, e);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
else if (CLIENT_STRATEGY?.loadComponentForMount) {
|
|
535
|
+
comp = await CLIENT_STRATEGY.loadComponentForMount(candidate, 'initial_mount');
|
|
501
536
|
}
|
|
502
537
|
if (comp) {
|
|
503
538
|
const ok = await performResetRoot(comp);
|
|
@@ -558,11 +593,7 @@ function applyDelta(payload) {
|
|
|
558
593
|
setGraphVersion(payload.newVersion);
|
|
559
594
|
}
|
|
560
595
|
const changed = payload.changed || [];
|
|
561
|
-
|
|
562
|
-
case 'vue':
|
|
563
|
-
recordVuePayloadChanges(changed, getGraphVersion());
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
596
|
+
CLIENT_STRATEGY?.recordPayloadChanges?.(changed, getGraphVersion());
|
|
566
597
|
(payload.changed || []).forEach((m) => {
|
|
567
598
|
if (!m || !m.id)
|
|
568
599
|
return;
|
|
@@ -629,7 +660,7 @@ function applyDelta(payload) {
|
|
|
629
660
|
}
|
|
630
661
|
if (isAppMainEntryId(id)) {
|
|
631
662
|
try {
|
|
632
|
-
const exists =
|
|
663
|
+
const exists = getGlobalScope().require?.(id) || globalThis.__nsGetModuleExports?.(id);
|
|
633
664
|
if (!exists && VERBOSE)
|
|
634
665
|
console.log(`[hmr][delta] skipping unresolved ${APP_MAIN_ENTRY_SPEC} change`);
|
|
635
666
|
if (!exists)
|
|
@@ -645,14 +676,10 @@ function applyDelta(payload) {
|
|
|
645
676
|
processQueue();
|
|
646
677
|
}
|
|
647
678
|
}
|
|
648
|
-
// Deterministic navigation using the current Vue app instance rather than vendor-held rootApp
|
|
679
|
+
// Deterministic navigation using the current Vue app instance rather than vendor-held rootApp.
|
|
649
680
|
function __nsNavigateUsingApp(comp, opts = {}) {
|
|
650
|
-
const g =
|
|
651
|
-
|
|
652
|
-
case 'vue':
|
|
653
|
-
ensureVueGlobals();
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
681
|
+
const g = getGlobalScope();
|
|
682
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
656
683
|
const AppFactory = g.createApp;
|
|
657
684
|
const RootCtor = g.NSVRoot;
|
|
658
685
|
if (typeof AppFactory !== 'function' || typeof RootCtor !== 'function') {
|
|
@@ -675,12 +702,14 @@ function __nsNavigateUsingApp(comp, opts = {}) {
|
|
|
675
702
|
const buildTarget = () => {
|
|
676
703
|
const existingApp = getCurrentApp();
|
|
677
704
|
const baseProvides = (existingApp && existingApp._context && existingApp._context.provides) || {};
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
705
|
+
// Forward `opts.props` as Vue's rootProps so `$navigateTo(Comp, { props: { … } })`
|
|
706
|
+
// reaches the destination component. nativescript-vue's stock `$navigateTo`
|
|
707
|
+
// does the same via `createNativeView(target, options?.props, …)` →
|
|
708
|
+
// `renderer.createApp(component, props)`. Dropping props here would surface
|
|
709
|
+
// at the destination as `[Vue warn]: Missing required prop` and any
|
|
710
|
+
// required-prop component would render with `undefined` bindings.
|
|
711
|
+
const app = AppFactory(normalizeComponent(comp, comp && (comp.__name || comp.name)), opts && opts.props);
|
|
712
|
+
CLIENT_STRATEGY?.onNavAppCreated?.(app);
|
|
684
713
|
try {
|
|
685
714
|
const registry = g.__nsVendorRegistry;
|
|
686
715
|
const req = registry?.get ? g.__nsVendorRequire || g.__nsRequire || g.require : g.__nsRequire || g.require;
|
|
@@ -775,6 +804,184 @@ try {
|
|
|
775
804
|
globalThis.__nsNavigateUsingApp = __nsNavigateUsingApp;
|
|
776
805
|
}
|
|
777
806
|
catch { }
|
|
807
|
+
const openModalRecords = [];
|
|
808
|
+
let modalTrackingInstalled = false;
|
|
809
|
+
/**
|
|
810
|
+
* Map a served/graph module id (e.g. `/app/modal-page.xml`) to its app-root
|
|
811
|
+
* relative path (`modal-page.xml`). Single mapping point — the raw-asset
|
|
812
|
+
* re-registration, page-navigation targets, and modal matching all derive
|
|
813
|
+
* from this; keep them in sync by construction.
|
|
814
|
+
*/
|
|
815
|
+
function toAppRelativePath(id) {
|
|
816
|
+
try {
|
|
817
|
+
const spec = normalizeSpec(id);
|
|
818
|
+
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
819
|
+
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
820
|
+
if (relPath.startsWith(appVirtual))
|
|
821
|
+
relPath = relPath.slice(appVirtual.length);
|
|
822
|
+
return relPath || null;
|
|
823
|
+
}
|
|
824
|
+
catch {
|
|
825
|
+
return null;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
/** App-root relative module name (no extension) for page-shaped files, else null. */
|
|
829
|
+
function toAppModuleName(id) {
|
|
830
|
+
const relPath = toAppRelativePath(id);
|
|
831
|
+
if (!relPath || !/\.(xml|ts|js)$/i.test(relPath))
|
|
832
|
+
return null;
|
|
833
|
+
return relPath.replace(/\.(xml|ts|js)$/i, '');
|
|
834
|
+
}
|
|
835
|
+
function ensureModalTracking() {
|
|
836
|
+
if (modalTrackingInstalled)
|
|
837
|
+
return;
|
|
838
|
+
try {
|
|
839
|
+
const View = getCore('View') || getGlobalScope().View;
|
|
840
|
+
const proto = View?.prototype;
|
|
841
|
+
if (!proto || typeof proto.showModal !== 'function')
|
|
842
|
+
return;
|
|
843
|
+
const orig = proto.showModal;
|
|
844
|
+
if (orig.__nsHmrModalTracked) {
|
|
845
|
+
modalTrackingInstalled = true;
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
848
|
+
const wrapped = function (...args) {
|
|
849
|
+
const result = orig.apply(this, args);
|
|
850
|
+
try {
|
|
851
|
+
if (typeof args[0] === 'string' && result) {
|
|
852
|
+
// Mirror core's getModalOptions arg shapes: (moduleName, options)
|
|
853
|
+
// or the deprecated positional form.
|
|
854
|
+
const options = args.length === 2 && args[1] && typeof args[1] === 'object' ? args[1] : { context: args[1], closeCallback: args[2], fullscreen: args[3], animated: args[4], stretched: args[5] };
|
|
855
|
+
const moduleName = String(args[0])
|
|
856
|
+
.replace(/^\.\//, '')
|
|
857
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
858
|
+
openModalRecords.push({ moduleName, options, parent: this, modal: result });
|
|
859
|
+
if (VERBOSE)
|
|
860
|
+
console.log('[hmr][modal] tracked open modal', moduleName);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
catch { }
|
|
864
|
+
return result;
|
|
865
|
+
};
|
|
866
|
+
wrapped.__nsHmrModalTracked = true;
|
|
867
|
+
proto.showModal = wrapped;
|
|
868
|
+
modalTrackingInstalled = true;
|
|
869
|
+
}
|
|
870
|
+
catch (e) {
|
|
871
|
+
if (VERBOSE)
|
|
872
|
+
console.warn('[hmr][modal] tracking install failed', e);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Enumerate the modals that are currently presented AND were opened by module
|
|
877
|
+
* name, with everything needed to re-present them.
|
|
878
|
+
*
|
|
879
|
+
* Source of truth is core's live modal stack (`_getRootModalViews()`):
|
|
880
|
+
* - `modal._moduleName` — set by the Builder on every createViewFromEntry
|
|
881
|
+
* view (longstanding, used by livesync), so available on any core.
|
|
882
|
+
* - `modal._modalOptions` — the original ShowModalOptions, stored by core's
|
|
883
|
+
* `_showNativeModalView` (newer cores). For older cores the showModal
|
|
884
|
+
* wrap's records (see ensureModalTracking) fill the gap.
|
|
885
|
+
* - `modal._modalParent` — the presenting view.
|
|
886
|
+
* Stale wrap records are pruned against the live stack while we're here.
|
|
887
|
+
*/
|
|
888
|
+
function getOpenStringModuleModals() {
|
|
889
|
+
const out = [];
|
|
890
|
+
try {
|
|
891
|
+
const App = getCore('Application');
|
|
892
|
+
const root = App?.getRootView?.() || App?._rootView;
|
|
893
|
+
const stack = root?._getRootModalViews?.() || [];
|
|
894
|
+
// Prune wrap records whose modal is gone (keeps the fallback list small).
|
|
895
|
+
for (let i = openModalRecords.length - 1; i >= 0; i--) {
|
|
896
|
+
if (!stack.includes(openModalRecords[i].modal)) {
|
|
897
|
+
openModalRecords.splice(i, 1);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
for (const modal of stack) {
|
|
901
|
+
const record = openModalRecords.find((r) => r.modal === modal);
|
|
902
|
+
const rawModuleName = typeof modal?._moduleName === 'string' && modal._moduleName ? modal._moduleName : record?.moduleName;
|
|
903
|
+
const parent = modal?._modalParent || record?.parent;
|
|
904
|
+
const options = modal?._modalOptions || record?.options;
|
|
905
|
+
if (!rawModuleName || !parent)
|
|
906
|
+
continue;
|
|
907
|
+
const moduleName = String(rawModuleName)
|
|
908
|
+
.replace(/^\.\//, '')
|
|
909
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
910
|
+
out.push({ moduleName, options, parent, modal });
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
catch (e) {
|
|
914
|
+
if (VERBOSE)
|
|
915
|
+
console.warn('[hmr][modal] open-modal enumeration failed', e);
|
|
916
|
+
}
|
|
917
|
+
return out;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Close and re-present an open modal so it rebuilds from the freshly
|
|
921
|
+
* re-registered XML/code-behind. Core clears the modal stack synchronously on
|
|
922
|
+
* close but the NATIVE dismissal completes asynchronously; iOS refuses a
|
|
923
|
+
* present while a dismissal is in flight. Newer cores fire `closedModally` on
|
|
924
|
+
* the modal at exactly that completion point — preferred signal. Older cores
|
|
925
|
+
* fall back to polling `isLoaded` (flipped by `_tearDownUI` in the same
|
|
926
|
+
* completion callback).
|
|
927
|
+
*/
|
|
928
|
+
async function reshowOpenModal(record) {
|
|
929
|
+
const { parent, modal, moduleName, options } = record;
|
|
930
|
+
await new Promise((resolve) => {
|
|
931
|
+
let settled = false;
|
|
932
|
+
const finish = () => {
|
|
933
|
+
if (!settled) {
|
|
934
|
+
settled = true;
|
|
935
|
+
resolve();
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
let eventArmed = false;
|
|
939
|
+
try {
|
|
940
|
+
if (typeof modal.once === 'function') {
|
|
941
|
+
modal.once('closedModally', finish);
|
|
942
|
+
eventArmed = true;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
catch { }
|
|
946
|
+
// Poll fallback (also the safety net if the event never fires —
|
|
947
|
+
// e.g. an interactive-dismiss cancellation).
|
|
948
|
+
const deadline = Date.now() + 2000;
|
|
949
|
+
const poll = () => {
|
|
950
|
+
if (settled)
|
|
951
|
+
return;
|
|
952
|
+
let stillLoaded = false;
|
|
953
|
+
try {
|
|
954
|
+
stillLoaded = !!modal.isLoaded;
|
|
955
|
+
}
|
|
956
|
+
catch { }
|
|
957
|
+
if ((!eventArmed && !stillLoaded) || Date.now() > deadline) {
|
|
958
|
+
finish();
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
setTimeout(poll, 50);
|
|
962
|
+
};
|
|
963
|
+
setTimeout(poll, 50);
|
|
964
|
+
try {
|
|
965
|
+
modal.closeModal();
|
|
966
|
+
}
|
|
967
|
+
catch (e) {
|
|
968
|
+
if (VERBOSE)
|
|
969
|
+
console.warn('[hmr][modal] close failed for', moduleName, e);
|
|
970
|
+
finish();
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
// One settle beat so the platform finishes releasing the presentation
|
|
974
|
+
// before the new present begins.
|
|
975
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
976
|
+
try {
|
|
977
|
+
parent.showModal(moduleName, { ...(options || {}), animated: false });
|
|
978
|
+
if (VERBOSE)
|
|
979
|
+
console.log('[hmr][modal] re-presented', moduleName);
|
|
980
|
+
}
|
|
981
|
+
catch (e) {
|
|
982
|
+
console.warn('[hmr][modal] re-present failed for', moduleName, e);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
778
985
|
async function processQueue() {
|
|
779
986
|
if (!globalThis.__NS_HMR_BOOT_COMPLETE__) {
|
|
780
987
|
if (VERBOSE)
|
|
@@ -810,7 +1017,43 @@ async function processQueue() {
|
|
|
810
1017
|
return;
|
|
811
1018
|
if (VERBOSE)
|
|
812
1019
|
console.log('[hmr][queue] processing changed ids', drained);
|
|
1020
|
+
// Track wall-clock so the 'complete' frame can show a meaningful
|
|
1021
|
+
// total. Only the Solid + TypeScript flavors drive the overlay
|
|
1022
|
+
// from here; Angular has its own flow inside
|
|
1023
|
+
// `frameworks/angular/client/index.ts`.
|
|
1024
|
+
const tQueueStart = Date.now();
|
|
1025
|
+
const driveSolidOverlay = TARGET_FLAVOR === 'solid';
|
|
1026
|
+
// Explicit eviction step.
|
|
1027
|
+
//
|
|
1028
|
+
// On modern runtimes the URL canonicalizer collapses any
|
|
1029
|
+
// `__ns_hmr__/<tag>/` segment back to a stable cache key, so
|
|
1030
|
+
// without explicit eviction the upcoming `import(url)` would
|
|
1031
|
+
// resolve via V8's `g_moduleRegistry` and return the cached
|
|
1032
|
+
// stale module — making the queue drain a silent no-op for
|
|
1033
|
+
// every save after the first.
|
|
1034
|
+
//
|
|
1035
|
+
// We hand the canonical eviction URLs to the runtime first;
|
|
1036
|
+
// `invalidateModulesByUrls` is a no-op on older runtimes and
|
|
1037
|
+
// `requestModuleFromServer` automatically falls back to the
|
|
1038
|
+
// legacy `/ns/m/__ns_hmr__/v<N>/` URL versioning path in that
|
|
1039
|
+
// case. node_modules and virtual specs are filtered out by
|
|
1040
|
+
// `buildEvictionUrls` so vendor modules stay hot.
|
|
1041
|
+
if (driveSolidOverlay) {
|
|
1042
|
+
setUpdateOverlayStage('evicting', {
|
|
1043
|
+
detail: drained.length === 1 ? `Invalidating ${drained[0]}` : `Invalidating ${drained.length} modules`,
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
const evictUrls = buildEvictionUrls(drained);
|
|
1047
|
+
const evicted = invalidateModulesByUrls(evictUrls);
|
|
1048
|
+
if (VERBOSE)
|
|
1049
|
+
console.log(`[hmr][queue] eviction count=${evictUrls.length} ok=${evicted}`);
|
|
813
1050
|
// Evaluate changed modules best-effort; failures shouldn't completely break HMR.
|
|
1051
|
+
if (driveSolidOverlay) {
|
|
1052
|
+
setUpdateOverlayStage('reimporting', {
|
|
1053
|
+
detail: drained.length === 1 ? `Re-importing ${drained[0]}` : `Re-importing ${drained.length} modules`,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
let reimportFailures = 0;
|
|
814
1057
|
for (const id of drained) {
|
|
815
1058
|
try {
|
|
816
1059
|
const spec = normalizeSpec(id);
|
|
@@ -820,18 +1063,56 @@ async function processQueue() {
|
|
|
820
1063
|
if (VERBOSE)
|
|
821
1064
|
console.log('[hmr][queue] re-import', { id, spec, url });
|
|
822
1065
|
const mod = await import(/* @vite-ignore */ url);
|
|
1066
|
+
// TS/XML flavor: refresh the bundler module registry with the fresh
|
|
1067
|
+
// exports so Builder.createViewFromEntry / loadModule('<page>')
|
|
1068
|
+
// resolves the NEW code-behind (tap handlers, page events) instead
|
|
1069
|
+
// of the stale module captured in the boot bundle. Without this,
|
|
1070
|
+
// XML re-renders pick up new markup but keep old behavior.
|
|
1071
|
+
if (TARGET_FLAVOR === 'typescript' && mod && /\.(ts|js)$/i.test(id)) {
|
|
1072
|
+
try {
|
|
1073
|
+
const g = getGlobalScope();
|
|
1074
|
+
const moduleName = toAppModuleName(id);
|
|
1075
|
+
if (moduleName && typeof g.registerModule === 'function') {
|
|
1076
|
+
g.registerModule(moduleName, () => mod);
|
|
1077
|
+
g.registerModule('./' + moduleName, () => mod);
|
|
1078
|
+
if (VERBOSE)
|
|
1079
|
+
console.log('[hmr][queue] re-registered code-behind', moduleName);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
catch (e) {
|
|
1083
|
+
if (VERBOSE)
|
|
1084
|
+
console.warn('[hmr][queue] code-behind re-register failed for', id, e);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
823
1087
|
}
|
|
824
1088
|
catch (e) {
|
|
825
|
-
|
|
826
|
-
|
|
1089
|
+
// Never silent: a failed re-import means the device may keep
|
|
1090
|
+
// running the previous module body, so always surface it (not
|
|
1091
|
+
// verbose-gated) — otherwise the overlay reports success while
|
|
1092
|
+
// the app runs stale code.
|
|
1093
|
+
reimportFailures++;
|
|
1094
|
+
console.warn('[hmr][queue] re-import FAILED for', id, '-', e?.message ?? e);
|
|
827
1095
|
}
|
|
828
1096
|
}
|
|
1097
|
+
if (reimportFailures > 0) {
|
|
1098
|
+
console.warn(`[hmr][queue] ${reimportFailures}/${drained.length} module(s) failed to re-import; the applied update may be incomplete.`);
|
|
1099
|
+
}
|
|
829
1100
|
// After evaluating the batch, perform flavor-specific UI refresh.
|
|
830
1101
|
switch (TARGET_FLAVOR) {
|
|
831
1102
|
case 'vue':
|
|
832
|
-
//
|
|
1103
|
+
// graph + performResetRoot + getOverlay let the Vue strategy
|
|
1104
|
+
// propagate non-SFC dep changes to the nearest `.vue` boundary
|
|
1105
|
+
// and remount it (see `propagateDepChangeToSfcBoundary`).
|
|
1106
|
+
await CLIENT_STRATEGY?.refreshAfterBatch?.(drained, { setUpdateOverlayStage, startedAt: tQueueStart, graph, performResetRoot, getOverlay: getHmrOverlayApi });
|
|
833
1107
|
break;
|
|
834
1108
|
case 'solid': {
|
|
1109
|
+
// Boundaries discovered in this HMR cycle (tsx files reachable
|
|
1110
|
+
// via the reverse import graph from any changed file, plus route
|
|
1111
|
+
// files reachable from any tsx start point). Declared at the top
|
|
1112
|
+
// of the case block so the emit step below can include the
|
|
1113
|
+
// complete set in the listener event — framework integrations
|
|
1114
|
+
// use it to map route boundaries → fresh component references.
|
|
1115
|
+
const boundaries = new Set();
|
|
835
1116
|
// Solid .tsx components are self-accepting via solid-refresh's inline
|
|
836
1117
|
// patchRegistry — re-importing them is sufficient. For non-component
|
|
837
1118
|
// .ts utility modules, we must propagate up the import graph to find
|
|
@@ -850,8 +1131,10 @@ async function processQueue() {
|
|
|
850
1131
|
arr.push(id);
|
|
851
1132
|
}
|
|
852
1133
|
}
|
|
853
|
-
// BFS from each non-tsx changed module up to tsx/jsx
|
|
854
|
-
|
|
1134
|
+
// Pass 1: BFS from each non-tsx changed module up to tsx/jsx
|
|
1135
|
+
// boundaries. These get re-imported below so solid-refresh's
|
|
1136
|
+
// inline patchRegistry runs and (best-effort) swaps the proxy
|
|
1137
|
+
// signals for any components defined in those tsx boundaries.
|
|
855
1138
|
for (const id of drained) {
|
|
856
1139
|
if (/\.(tsx|jsx)$/i.test(id))
|
|
857
1140
|
continue; // already self-accepting
|
|
@@ -875,6 +1158,51 @@ async function processQueue() {
|
|
|
875
1158
|
}
|
|
876
1159
|
}
|
|
877
1160
|
}
|
|
1161
|
+
// Pass 2: walk further from any tsx starting point (a tsx file
|
|
1162
|
+
// in `drained` OR a tsx boundary discovered in pass 1) to find
|
|
1163
|
+
// route files (`/src/routes/*.{tsx,jsx}`) that transitively
|
|
1164
|
+
// import them. Re-importing a route file refreshes its
|
|
1165
|
+
// `Route.options.component` to the freshly-imported reference
|
|
1166
|
+
// and the existing boundary loop below patches the live router
|
|
1167
|
+
// with that fresh reference.
|
|
1168
|
+
//
|
|
1169
|
+
// This is the key fix for "edit home.tsx → save → no visual
|
|
1170
|
+
// update": the old BFS skipped tsx files in `drained` (assuming
|
|
1171
|
+
// solid-refresh's in-place proxy patch was sufficient), but in
|
|
1172
|
+
// the universal-renderer + nested-context configuration that
|
|
1173
|
+
// patch does not always propagate to the visible page tree.
|
|
1174
|
+
// Adding the route file as a boundary lets us patch
|
|
1175
|
+
// `route.options.component` directly to a fresh module export,
|
|
1176
|
+
// which the framework subscriber then passes through to the
|
|
1177
|
+
// page remount — making the cycle robust to the proxy patch
|
|
1178
|
+
// silently failing.
|
|
1179
|
+
const tsxStarts = new Set();
|
|
1180
|
+
for (const id of drained) {
|
|
1181
|
+
if (/\.(tsx|jsx)$/i.test(id))
|
|
1182
|
+
tsxStarts.add(id);
|
|
1183
|
+
}
|
|
1184
|
+
for (const b of boundaries)
|
|
1185
|
+
tsxStarts.add(b);
|
|
1186
|
+
const ROUTE_FILE_RE = /\/src\/routes\/.+\.(tsx|jsx)$/i;
|
|
1187
|
+
for (const start of tsxStarts) {
|
|
1188
|
+
const visited = new Set();
|
|
1189
|
+
const queue = [start];
|
|
1190
|
+
while (queue.length) {
|
|
1191
|
+
const cur = queue.shift();
|
|
1192
|
+
if (visited.has(cur))
|
|
1193
|
+
continue;
|
|
1194
|
+
visited.add(cur);
|
|
1195
|
+
if (cur !== start && ROUTE_FILE_RE.test(cur)) {
|
|
1196
|
+
boundaries.add(cur);
|
|
1197
|
+
}
|
|
1198
|
+
const importers = reverseIndex.get(cur);
|
|
1199
|
+
if (!importers)
|
|
1200
|
+
continue;
|
|
1201
|
+
for (const imp of importers) {
|
|
1202
|
+
queue.push(imp);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
878
1206
|
// Re-import each boundary so solid-refresh patchRegistry fires.
|
|
879
1207
|
// For route files (TanStack Router), capture the new Route export
|
|
880
1208
|
// and patch the router's existing route with the fresh loader.
|
|
@@ -885,7 +1213,7 @@ async function processQueue() {
|
|
|
885
1213
|
const findRouter = () => {
|
|
886
1214
|
if (discoveredRouter)
|
|
887
1215
|
return discoveredRouter;
|
|
888
|
-
const g =
|
|
1216
|
+
const g = getGlobalScope();
|
|
889
1217
|
if (g.__ns_router?.routesById)
|
|
890
1218
|
return (discoveredRouter = g.__ns_router);
|
|
891
1219
|
// Fallback: scan common global keys for router
|
|
@@ -924,6 +1252,15 @@ async function processQueue() {
|
|
|
924
1252
|
}
|
|
925
1253
|
return null;
|
|
926
1254
|
};
|
|
1255
|
+
// Evict the boundary set so re-importing each .tsx
|
|
1256
|
+
// component actually picks up the new transitive
|
|
1257
|
+
// dependency code; without this V8 returns the
|
|
1258
|
+
// cached boundary module unchanged.
|
|
1259
|
+
const boundaryIds = Array.from(boundaries);
|
|
1260
|
+
const solidEvictUrls = buildEvictionUrls(boundaryIds);
|
|
1261
|
+
const solidEvicted = invalidateModulesByUrls(solidEvictUrls);
|
|
1262
|
+
if (VERBOSE)
|
|
1263
|
+
console.log(`[hmr][solid] eviction count=${solidEvictUrls.length} ok=${solidEvicted}`);
|
|
927
1264
|
for (const id of boundaries) {
|
|
928
1265
|
if (seen.has(id))
|
|
929
1266
|
continue;
|
|
@@ -935,22 +1272,28 @@ async function processQueue() {
|
|
|
935
1272
|
if (VERBOSE)
|
|
936
1273
|
console.log('[hmr][solid] propagated to boundary', { id, url });
|
|
937
1274
|
const mod = await import(/* @vite-ignore */ url);
|
|
938
|
-
// Patch TanStack Router route
|
|
1275
|
+
// Patch TanStack Router route options for any module
|
|
1276
|
+
// that exports a `Route`. We patch BOTH the component
|
|
1277
|
+
// and the loader (when present); components-only routes
|
|
1278
|
+
// were previously skipped because the gate required a
|
|
1279
|
+
// loader, which left their `options.component` pointing
|
|
1280
|
+
// at the stale module's exports after HMR.
|
|
939
1281
|
try {
|
|
940
1282
|
const newRoute = mod?.Route;
|
|
941
|
-
if (newRoute?.options
|
|
1283
|
+
if (newRoute?.options) {
|
|
942
1284
|
const router = findRouter();
|
|
943
1285
|
const fullPath = boundaryToFullPath(id);
|
|
944
1286
|
if (VERBOSE)
|
|
945
|
-
console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router,
|
|
1287
|
+
console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router, hasLoader: !!newRoute.options.loader, hasComponent: !!newRoute.options.component });
|
|
946
1288
|
const existingRoute = fullPath && router ? findRouteByFullPath(router, fullPath) : null;
|
|
947
1289
|
if (existingRoute?.options) {
|
|
948
|
-
|
|
1290
|
+
if (newRoute.options.loader)
|
|
1291
|
+
existingRoute.options.loader = newRoute.options.loader;
|
|
949
1292
|
if (newRoute.options.component)
|
|
950
1293
|
existingRoute.options.component = newRoute.options.component;
|
|
951
1294
|
routesPatchCount++;
|
|
952
1295
|
if (VERBOSE)
|
|
953
|
-
console.log('[hmr][solid] patched route
|
|
1296
|
+
console.log('[hmr][solid] patched route', existingRoute.id, 'fullPath=', fullPath);
|
|
954
1297
|
}
|
|
955
1298
|
else if (VERBOSE) {
|
|
956
1299
|
console.log('[hmr][solid] no matching route for fullPath', fullPath);
|
|
@@ -986,6 +1329,44 @@ async function processQueue() {
|
|
|
986
1329
|
if (VERBOSE)
|
|
987
1330
|
console.warn('[hmr][solid] propagation failed', e);
|
|
988
1331
|
}
|
|
1332
|
+
// Notify any framework integrations (e.g.
|
|
1333
|
+
// `@nativescript/tanstack-router`) that a Solid HMR
|
|
1334
|
+
// cycle has completed. They use this signal to perform
|
|
1335
|
+
// framework-specific UI refresh (e.g. remount the active
|
|
1336
|
+
// router page) when solid-refresh's own reactive
|
|
1337
|
+
// propagation does not reach the visible tree under
|
|
1338
|
+
// the current renderer/context configuration.
|
|
1339
|
+
//
|
|
1340
|
+
// Boundaries include both the directly-changed tsx files
|
|
1341
|
+
// AND every tsx ancestor reachable via the reverse import
|
|
1342
|
+
// graph (route files in particular). The framework
|
|
1343
|
+
// listener uses the route-file boundaries to look up the
|
|
1344
|
+
// freshly-patched `route.options.component` and pass it
|
|
1345
|
+
// through to the page remount.
|
|
1346
|
+
try {
|
|
1347
|
+
const tsxChangedInDrained = drained.filter((id) => /\.(tsx|jsx)$/i.test(id));
|
|
1348
|
+
const allBoundaries = Array.from(new Set([...tsxChangedInDrained, ...boundaries]));
|
|
1349
|
+
nsSolidHmrEmit({
|
|
1350
|
+
kind: 'solid',
|
|
1351
|
+
changedFiles: drained.slice(),
|
|
1352
|
+
boundaries: allBoundaries,
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
catch (err) {
|
|
1356
|
+
if (VERBOSE)
|
|
1357
|
+
console.warn('[hmr][solid] emit failed', err);
|
|
1358
|
+
}
|
|
1359
|
+
// Tell the overlay the cycle is done. solid-refresh's
|
|
1360
|
+
// inline patchRegistry has already flushed the new
|
|
1361
|
+
// component bodies into the live tree (the `case
|
|
1362
|
+
// 'solid'` block above re-imports each .tsx
|
|
1363
|
+
// boundary), so by the time we get here the user is
|
|
1364
|
+
// already looking at the new render. The 'complete'
|
|
1365
|
+
// frame surfaces the wall-clock total and triggers
|
|
1366
|
+
// the overlay's auto-hide.
|
|
1367
|
+
setUpdateOverlayStage('complete', {
|
|
1368
|
+
detail: `Total ${Math.max(0, Date.now() - tQueueStart)}ms`,
|
|
1369
|
+
});
|
|
989
1370
|
break;
|
|
990
1371
|
}
|
|
991
1372
|
case 'typescript': {
|
|
@@ -993,7 +1374,7 @@ async function processQueue() {
|
|
|
993
1374
|
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
994
1375
|
// own bootstrapping wires up via `Application.run`.
|
|
995
1376
|
try {
|
|
996
|
-
const g =
|
|
1377
|
+
const g = getGlobalScope();
|
|
997
1378
|
const App = getCore('Application') || g.Application;
|
|
998
1379
|
if (!App || typeof App.resetRootView !== 'function') {
|
|
999
1380
|
if (VERBOSE)
|
|
@@ -1019,10 +1400,9 @@ async function processQueue() {
|
|
|
1019
1400
|
const rawContent = await resp.text();
|
|
1020
1401
|
// Register under all nickname variants the module registry uses.
|
|
1021
1402
|
// The bundler context registers XML as e.g., './main-page.xml' and 'main-page.xml'
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
relPath = relPath.slice(appVirtual.length);
|
|
1403
|
+
const relPath = toAppRelativePath(id);
|
|
1404
|
+
if (!relPath)
|
|
1405
|
+
continue;
|
|
1026
1406
|
const nicknames = ['./' + relPath, relPath];
|
|
1027
1407
|
// Also add without extension for CSS
|
|
1028
1408
|
const extIdx = relPath.lastIndexOf('.');
|
|
@@ -1048,21 +1428,24 @@ async function processQueue() {
|
|
|
1048
1428
|
}
|
|
1049
1429
|
}
|
|
1050
1430
|
}
|
|
1431
|
+
// Modal-aware refresh: pages currently PRESENTED AS MODALS must be
|
|
1432
|
+
// closed + re-presented in place — navigating the top frame to a
|
|
1433
|
+
// modal's page would push it as a frame page, and resetRootView
|
|
1434
|
+
// would dismiss the modal entirely. State comes from core's live
|
|
1435
|
+
// modal stack (_moduleName/_modalOptions/_modalParent); the
|
|
1436
|
+
// showModal wrap only backfills options on older cores.
|
|
1437
|
+
ensureModalTracking();
|
|
1438
|
+
const openModals = getOpenStringModuleModals();
|
|
1439
|
+
const changedModuleNames = new Set(drained.map(toAppModuleName).filter(Boolean));
|
|
1440
|
+
const modalsToReshow = openModals.filter((record) => changedModuleNames.has(record.moduleName));
|
|
1441
|
+
const reshowModuleNames = new Set(modalsToReshow.map((record) => record.moduleName));
|
|
1051
1442
|
// Determine if we can navigate in-place to a changed page
|
|
1052
1443
|
// instead of resetting all the way back to app-root.
|
|
1053
1444
|
// This keeps the user on the page they're editing for faster iteration.
|
|
1054
1445
|
const changedXmlPages = drained
|
|
1055
1446
|
.filter((id) => /\.xml$/i.test(id))
|
|
1056
|
-
.map((id) =>
|
|
1057
|
-
|
|
1058
|
-
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
1059
|
-
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
1060
|
-
if (relPath.startsWith(appVirtual))
|
|
1061
|
-
relPath = relPath.slice(appVirtual.length);
|
|
1062
|
-
// Strip .xml extension to get the moduleName (e.g., 'pages/status-bar')
|
|
1063
|
-
return relPath.replace(/\.xml$/i, '');
|
|
1064
|
-
})
|
|
1065
|
-
.filter((m) => m && m !== 'app-root');
|
|
1447
|
+
.map((id) => toAppModuleName(id))
|
|
1448
|
+
.filter((m) => m && m !== 'app-root' && !reshowModuleNames.has(m));
|
|
1066
1449
|
// Resolve the topmost Frame from the bundled realm.
|
|
1067
1450
|
// Frame.topmost() relies on an internal frameStack array, so we must
|
|
1068
1451
|
// call it on the bundled-realm class. Multiple strategies to find it:
|
|
@@ -1098,7 +1481,7 @@ async function processQueue() {
|
|
|
1098
1481
|
catch { }
|
|
1099
1482
|
}
|
|
1100
1483
|
if (VERBOSE)
|
|
1101
|
-
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame);
|
|
1484
|
+
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame, 'modalsToReshow=', modalsToReshow.length);
|
|
1102
1485
|
if (changedXmlPages.length > 0 && topFrame) {
|
|
1103
1486
|
// Navigate the current frame to the changed page directly.
|
|
1104
1487
|
// Use the last changed XML page (most specific).
|
|
@@ -1113,15 +1496,32 @@ async function processQueue() {
|
|
|
1113
1496
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1114
1497
|
}
|
|
1115
1498
|
}
|
|
1116
|
-
else {
|
|
1499
|
+
else if (modalsToReshow.length === 0) {
|
|
1500
|
+
// No frame page to refresh and no open modal owns the change —
|
|
1501
|
+
// fall back to a full root reset. (Skipped when an open modal is
|
|
1502
|
+
// being re-presented below: resetRootView would dismiss it.)
|
|
1117
1503
|
if (VERBOSE)
|
|
1118
1504
|
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
1119
1505
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1120
1506
|
}
|
|
1507
|
+
// Re-present any open modals whose XML/code-behind changed. The
|
|
1508
|
+
// modules were already re-registered above (raw XML assets + fresh
|
|
1509
|
+
// code-behind exports), so the re-presented modal rebuilds from
|
|
1510
|
+
// the new content while the page beneath it stays put.
|
|
1511
|
+
for (const record of modalsToReshow) {
|
|
1512
|
+
await reshowOpenModal(record);
|
|
1513
|
+
}
|
|
1121
1514
|
}
|
|
1122
1515
|
catch (e) {
|
|
1123
1516
|
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
1124
1517
|
}
|
|
1518
|
+
// Tell the overlay the cycle is done — same as the solid path
|
|
1519
|
+
// above. Without this the applying overlay sticks at
|
|
1520
|
+
// 'received' (5%) forever even though the in-place navigate /
|
|
1521
|
+
// resetRootView already applied the update.
|
|
1522
|
+
setUpdateOverlayStage('complete', {
|
|
1523
|
+
detail: `Total ${Math.max(0, Date.now() - tQueueStart)}ms`,
|
|
1524
|
+
});
|
|
1125
1525
|
break;
|
|
1126
1526
|
}
|
|
1127
1527
|
}
|
|
@@ -1129,11 +1529,37 @@ async function processQueue() {
|
|
|
1129
1529
|
finally {
|
|
1130
1530
|
processingQueue = false;
|
|
1131
1531
|
processingPromise = null;
|
|
1532
|
+
// If a delta arrived mid-cycle (pushed onto changedQueue while
|
|
1533
|
+
// processingQueue was still true, so its processQueue() call early-
|
|
1534
|
+
// returned), re-drain — otherwise that save is stranded until the next
|
|
1535
|
+
// delta. Deferred via setTimeout to avoid synchronous re-entrancy as
|
|
1536
|
+
// this promise settles.
|
|
1537
|
+
if (changedQueue.length) {
|
|
1538
|
+
setTimeout(() => {
|
|
1539
|
+
try {
|
|
1540
|
+
processQueue();
|
|
1541
|
+
}
|
|
1542
|
+
catch { }
|
|
1543
|
+
}, 0);
|
|
1544
|
+
}
|
|
1132
1545
|
}
|
|
1133
1546
|
})();
|
|
1134
1547
|
return processingPromise;
|
|
1135
1548
|
}
|
|
1136
1549
|
let hmrSocket = null;
|
|
1550
|
+
// Single reconnect timer. Overlapping close/timeout events used to each schedule
|
|
1551
|
+
// their own `setTimeout(connectHmr, …)`, stacking multiple pending reconnects
|
|
1552
|
+
// that could spawn (and leak) duplicate sockets/listeners. Route all reconnect
|
|
1553
|
+
// scheduling through here so only one is ever pending.
|
|
1554
|
+
let reconnectTimer = null;
|
|
1555
|
+
function scheduleReconnect(delayMs) {
|
|
1556
|
+
if (reconnectTimer)
|
|
1557
|
+
clearTimeout(reconnectTimer);
|
|
1558
|
+
reconnectTimer = setTimeout(() => {
|
|
1559
|
+
reconnectTimer = null;
|
|
1560
|
+
connectHmr();
|
|
1561
|
+
}, delayMs);
|
|
1562
|
+
}
|
|
1137
1563
|
// Track server-announced batches for each version so we can import in-order client-side
|
|
1138
1564
|
const txnClientBatches = new Map();
|
|
1139
1565
|
// Public hook for NativeScript runtime to call from ImportModuleDynamicallyCallback later.
|
|
@@ -1161,10 +1587,16 @@ function connectHmr() {
|
|
|
1161
1587
|
if (hmrSocket?.readyState === WebSocket.OPEN)
|
|
1162
1588
|
return;
|
|
1163
1589
|
if (hmrSocket?.readyState === WebSocket.CONNECTING) {
|
|
1164
|
-
if (
|
|
1590
|
+
if (VERBOSE)
|
|
1165
1591
|
console.log('[hmr-client] Already connecting to HMR WebSocket, skipping');
|
|
1166
1592
|
return;
|
|
1167
1593
|
}
|
|
1594
|
+
// A reconnect fired (or a manual connect raced one) — cancel any other pending
|
|
1595
|
+
// reconnect so we don't end up with overlapping connect attempts.
|
|
1596
|
+
if (reconnectTimer) {
|
|
1597
|
+
clearTimeout(reconnectTimer);
|
|
1598
|
+
reconnectTimer = null;
|
|
1599
|
+
}
|
|
1168
1600
|
try {
|
|
1169
1601
|
globalThis.__NS_HMR_CLIENT_SOCKET_READY__ = false;
|
|
1170
1602
|
}
|
|
@@ -1172,7 +1604,7 @@ function connectHmr() {
|
|
|
1172
1604
|
const overlayStage = hasOpenedHmrSocket ? 'reconnecting' : 'connecting';
|
|
1173
1605
|
const baseUrl = getHMRWsUrl() || 'ws://localhost:5173/ns-hmr';
|
|
1174
1606
|
const buildCandidates = (url) => {
|
|
1175
|
-
|
|
1607
|
+
const candidates = [];
|
|
1176
1608
|
try {
|
|
1177
1609
|
const u = new URL(url);
|
|
1178
1610
|
const proto = u.protocol === 'wss:' ? ['wss'] : ['ws'];
|
|
@@ -1180,7 +1612,7 @@ function connectHmr() {
|
|
|
1180
1612
|
// Build ordered host candidates with preference to the active HTTP origin
|
|
1181
1613
|
const orderedHosts = [];
|
|
1182
1614
|
try {
|
|
1183
|
-
const g =
|
|
1615
|
+
const g = getGlobalScope();
|
|
1184
1616
|
const httpOrigin = g && typeof g.__NS_HTTP_ORIGIN__ === 'string' ? g.__NS_HTTP_ORIGIN__ : undefined;
|
|
1185
1617
|
if (httpOrigin) {
|
|
1186
1618
|
try {
|
|
@@ -1231,19 +1663,29 @@ function connectHmr() {
|
|
|
1231
1663
|
if (idx >= candidates.length) {
|
|
1232
1664
|
showConnectionOverlayNow('offline', 'Waiting for the Vite websocket to come back.');
|
|
1233
1665
|
console.warn('[hmr-client] All WS candidates failed:', candidates.join(', '));
|
|
1234
|
-
|
|
1666
|
+
scheduleReconnect(1500);
|
|
1235
1667
|
return;
|
|
1236
1668
|
}
|
|
1237
1669
|
const url = candidates[idx++];
|
|
1238
1670
|
const connectionDetail = `${overlayStage === 'reconnecting' ? 'Retrying' : 'Opening'} ${url}`;
|
|
1239
|
-
if (
|
|
1671
|
+
if (overlayStage === 'reconnecting' && shownConnectionOverlayStage === 'offline') {
|
|
1672
|
+
// Don't flip the visible 'offline' frame to 'reconnecting' for an
|
|
1673
|
+
// attempt that may fail instantly — defer it so only a genuinely
|
|
1674
|
+
// in-flight attempt surfaces. The deferred show is cancelled by
|
|
1675
|
+
// showConnectionOverlayNow (offline re-show / synchronizing) the
|
|
1676
|
+
// moment this attempt resolves. Keeps 'offline' stable instead of
|
|
1677
|
+
// flickering once per retry. (Checking the PAINTED stage, not the
|
|
1678
|
+
// pending one, so every candidate in the loop keeps deferring.)
|
|
1679
|
+
scheduleConnectionOverlay(overlayStage, connectionDetail, RECONNECTING_OVER_OFFLINE_DELAY_MS);
|
|
1680
|
+
}
|
|
1681
|
+
else if (connectionOverlayVisible) {
|
|
1240
1682
|
updateConnectionOverlay(overlayStage, connectionDetail);
|
|
1241
1683
|
}
|
|
1242
1684
|
else {
|
|
1243
1685
|
scheduleConnectionOverlay(overlayStage, connectionDetail);
|
|
1244
1686
|
}
|
|
1245
1687
|
try {
|
|
1246
|
-
if (
|
|
1688
|
+
if (VERBOSE)
|
|
1247
1689
|
console.log('[hmr-client] Connecting to HMR WebSocket:', url);
|
|
1248
1690
|
const sock = new WebSocket(url);
|
|
1249
1691
|
hmrSocket = sock;
|
|
@@ -1273,7 +1715,16 @@ function connectHmr() {
|
|
|
1273
1715
|
if (connectionOverlayVisible) {
|
|
1274
1716
|
showConnectionOverlayNow('synchronizing', 'Connected. Synchronizing the HMR graph.');
|
|
1275
1717
|
}
|
|
1276
|
-
|
|
1718
|
+
if (VERBOSE)
|
|
1719
|
+
console.log('[hmr-client] Connected to HMR WebSocket');
|
|
1720
|
+
// Print the active module reload mode once on first
|
|
1721
|
+
// successful connect so the user can correlate HMR latency
|
|
1722
|
+
// with runtime capability without grepping for protocol
|
|
1723
|
+
// details. The banner is verbose-gated.
|
|
1724
|
+
try {
|
|
1725
|
+
emitHmrModeBannerOnce();
|
|
1726
|
+
}
|
|
1727
|
+
catch { }
|
|
1277
1728
|
};
|
|
1278
1729
|
sock.onmessage = handleHmrMessage;
|
|
1279
1730
|
sock.onerror = (error) => {
|
|
@@ -1297,7 +1748,7 @@ function connectHmr() {
|
|
|
1297
1748
|
console.log('[hmr-client] WebSocket closed (code', ev?.code, '), will reconnect…');
|
|
1298
1749
|
scheduleConnectionOverlay('reconnecting', 'The websocket closed. Waiting to reconnect.', 700);
|
|
1299
1750
|
// try to reconnect with full candidate list again
|
|
1300
|
-
|
|
1751
|
+
scheduleReconnect(1000);
|
|
1301
1752
|
}
|
|
1302
1753
|
};
|
|
1303
1754
|
}
|
|
@@ -1333,10 +1784,26 @@ async function handleHmrMessage(ev) {
|
|
|
1333
1784
|
catch { }
|
|
1334
1785
|
}
|
|
1335
1786
|
if (msg) {
|
|
1787
|
+
// `ns:hmr-pending` is a fire-and-forget UX hint emitted by the
|
|
1788
|
+
// server at the START of handleHotUpdate. We drive the
|
|
1789
|
+
// HMR-applying overlay's 'received' frame here (synchronously),
|
|
1790
|
+
// well before the authoritative payload (`ns:angular-update` /
|
|
1791
|
+
// `ns:css-updates`) lands. Skip running any other handlers —
|
|
1792
|
+
// the pending message has no module payload and intentionally
|
|
1793
|
+
// does not bump the graph version.
|
|
1794
|
+
if (msg.type === 'ns:hmr-pending' && typeof msg.path === 'string') {
|
|
1795
|
+
setHmrPendingOverlay(msg.path);
|
|
1796
|
+
return;
|
|
1797
|
+
}
|
|
1798
|
+
// The per-flavor client strategy is loaded by a dynamic import(); make
|
|
1799
|
+
// sure it has resolved (and `install()` has run) before any handler that
|
|
1800
|
+
// delegates through it. After the first message this is an already-settled
|
|
1801
|
+
// promise (microtask only); for Solid/TypeScript it is `Promise.resolve()`.
|
|
1802
|
+
await CLIENT_STRATEGY_READY;
|
|
1336
1803
|
if (msg.type === 'ns:hmr-full-graph') {
|
|
1337
1804
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1338
1805
|
try {
|
|
1339
|
-
const g =
|
|
1806
|
+
const g = getGlobalScope();
|
|
1340
1807
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1341
1808
|
}
|
|
1342
1809
|
catch { }
|
|
@@ -1402,6 +1869,14 @@ async function handleHmrMessage(ev) {
|
|
|
1402
1869
|
});
|
|
1403
1870
|
if (toReimport.length && VERBOSE)
|
|
1404
1871
|
console.log('[hmr][full-graph] inferred changed modules; re-importing', toReimport);
|
|
1872
|
+
// Evict the inferred changed set before re-importing.
|
|
1873
|
+
// See `processQueue` for the architectural rationale; the
|
|
1874
|
+
// full-graph code path is the resync fallback (server chose
|
|
1875
|
+
// not to send a delta) and shares the same V8 cache pitfall.
|
|
1876
|
+
const fgEvictUrls = buildEvictionUrls(toReimport);
|
|
1877
|
+
const fgEvicted = invalidateModulesByUrls(fgEvictUrls);
|
|
1878
|
+
if (VERBOSE)
|
|
1879
|
+
console.log(`[hmr][full-graph] eviction count=${fgEvictUrls.length} ok=${fgEvicted}`);
|
|
1405
1880
|
for (const id of toReimport) {
|
|
1406
1881
|
try {
|
|
1407
1882
|
const spec = normalizeSpec(id);
|
|
@@ -1453,7 +1928,7 @@ async function handleHmrMessage(ev) {
|
|
|
1453
1928
|
if (msg.type === 'ns:hmr-delta') {
|
|
1454
1929
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1455
1930
|
try {
|
|
1456
|
-
const g =
|
|
1931
|
+
const g = getGlobalScope();
|
|
1457
1932
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1458
1933
|
}
|
|
1459
1934
|
catch { }
|
|
@@ -1475,10 +1950,108 @@ async function handleHmrMessage(ev) {
|
|
|
1475
1950
|
return;
|
|
1476
1951
|
}
|
|
1477
1952
|
else {
|
|
1953
|
+
// Vite custom-event dispatch.
|
|
1954
|
+
//
|
|
1955
|
+
// `server.ws.send('event-name', payload)` from any Vite plugin lands
|
|
1956
|
+
// on the wire as `{ type: 'custom', event: 'event-name', data: payload }`.
|
|
1957
|
+
// On the web, Vite's stock client owns a `customListenersMap` that
|
|
1958
|
+
// fires every `import.meta.hot.on('event-name', cb)` callback. We
|
|
1959
|
+
// don't run Vite's stock client on device — the iOS runtime owns
|
|
1960
|
+
// the listener registry via `__NS_DISPATCH_HOT_EVENT__` (the
|
|
1961
|
+
// counterpart to `import.meta.hot.on` populated by user code +
|
|
1962
|
+
// compiled Angular components). Forwarding `type: 'custom'` here
|
|
1963
|
+
// is the only thing standing between server-emitted events and
|
|
1964
|
+
// the listeners they were meant for.
|
|
1965
|
+
//
|
|
1966
|
+
// `angular:component-update` is the canonical example. Analog's
|
|
1967
|
+
// plugin sends it on `.html` / component-style edits; the
|
|
1968
|
+
// compiled component `.mjs` registered a listener that
|
|
1969
|
+
// dynamic-imports `/@ng/component?c=<id>&t=<ts>` and calls
|
|
1970
|
+
// `ɵɵreplaceMetadata` on the live class — swapping the template
|
|
1971
|
+
// definition AND walking live `LView`s to recreate matching views
|
|
1972
|
+
// in-place. The page stays mounted and only the changed bits
|
|
1973
|
+
// re-render. We MUST `return` after dispatch so the reboot path
|
|
1974
|
+
// (`handleAngularHotUpdateMessage` → `__reboot_ng_modules__`)
|
|
1975
|
+
// never runs for these updates — that's the whole point of the
|
|
1976
|
+
// component-replacement pipeline.
|
|
1977
|
+
//
|
|
1978
|
+
// All other custom events are forwarded but NOT short-circuited
|
|
1979
|
+
// (Vite spec: custom events are additive — they don't replace
|
|
1980
|
+
// any framework-specific handling). The reboot path falls through
|
|
1981
|
+
// for `ns:angular-update` (the legacy/`.ts`-edit broadcast) and
|
|
1982
|
+
// for any framework not yet using the in-place replacement path.
|
|
1983
|
+
if (msg.type === 'custom' && typeof msg.event === 'string') {
|
|
1984
|
+
// Dispatch every Vite "custom" event through the runtime's
|
|
1985
|
+
// `__NS_DISPATCH_HOT_EVENT__` bridge so `import.meta.hot.on(event, cb)`
|
|
1986
|
+
// callbacks fire on the device. Critical contract: this is the
|
|
1987
|
+
// ONLY route by which Analog's `angular:component-update` reaches
|
|
1988
|
+
// the compiled component's `(d) => d.id === id && Component_HmrLoad(...)`
|
|
1989
|
+
// listener — without it, server-side broadcasts log green
|
|
1990
|
+
// (`(client) hmr update`) while the device sees nothing happen.
|
|
1991
|
+
//
|
|
1992
|
+
// Diagnostic policy: log "no dispatcher" loud (boot-time rt-bridge
|
|
1993
|
+
// failure), and listener exceptions loud (compiled HmrLoad
|
|
1994
|
+
// fetch/parse error). Successful dispatches are silent — the
|
|
1995
|
+
// runtime's `[import.meta.hot] dispatch summary` line carries
|
|
1996
|
+
// the per-event match-count diagnostic.
|
|
1997
|
+
try {
|
|
1998
|
+
const dispatch = globalThis.__NS_DISPATCH_HOT_EVENT__;
|
|
1999
|
+
if (typeof dispatch === 'function') {
|
|
2000
|
+
dispatch(msg.event, msg.data);
|
|
2001
|
+
}
|
|
2002
|
+
else {
|
|
2003
|
+
console.warn(`[hmr-client][custom] no __NS_DISPATCH_HOT_EVENT__ available for '${msg.event}'`);
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
catch (err) {
|
|
2007
|
+
console.warn('[hmr-client][custom] dispatch threw for', msg.event, err);
|
|
2008
|
+
}
|
|
2009
|
+
if (msg.event === 'angular:component-update') {
|
|
2010
|
+
if (VERBOSE)
|
|
2011
|
+
console.log('[hmr-client][custom] dispatched angular:component-update — skipping reboot path');
|
|
2012
|
+
// Walk the apply-progress overlay through its
|
|
2013
|
+
// remaining stages for the in-place template-swap
|
|
2014
|
+
// path. The full reboot path
|
|
2015
|
+
// (`handleAngularHotUpdateMessage`) drives the
|
|
2016
|
+
// overlay itself ('received' → 'evicting' →
|
|
2017
|
+
// 'reimporting' → 'rebooting' → 'complete'); the
|
|
2018
|
+
// in-place path bypasses that handler entirely
|
|
2019
|
+
// because the work happens inside Angular's
|
|
2020
|
+
// `ɵɵreplaceMetadata` after the runtime forwards the
|
|
2021
|
+
// `angular:component-update` event to the compiled
|
|
2022
|
+
// component's listener. Without this update the
|
|
2023
|
+
// overlay would freeze at 5% ('received') even
|
|
2024
|
+
// though the visual swap completes a few frames
|
|
2025
|
+
// later — exactly the "Preparing update (5%)" stuck
|
|
2026
|
+
// frame we have been chasing.
|
|
2027
|
+
//
|
|
2028
|
+
// We transition straight to 'reimporting' to
|
|
2029
|
+
// communicate that metadata is being fetched (the
|
|
2030
|
+
// runtime listener fires `__ns_import('/@ng/component?c=...&t=...')`),
|
|
2031
|
+
// then schedule 'complete' on the next macrotask so
|
|
2032
|
+
// the auto-hide timer kicks in. The actual
|
|
2033
|
+
// template swap is fire-and-forget from this point;
|
|
2034
|
+
// the user sees the overlay close at the same time
|
|
2035
|
+
// as Angular re-renders the bound text/structure.
|
|
2036
|
+
try {
|
|
2037
|
+
const filePath = typeof msg.data?.id === 'string' ? decodeURIComponent(msg.data.id).split('@')[0] : undefined;
|
|
2038
|
+
const detail = filePath ? `Applying template update to ${filePath}` : 'Applying template update';
|
|
2039
|
+
setUpdateOverlayStage('reimporting', { detail });
|
|
2040
|
+
setTimeout(() => {
|
|
2041
|
+
try {
|
|
2042
|
+
setUpdateOverlayStage('complete', { detail: filePath ? `Updated ${filePath}` : 'Update applied' });
|
|
2043
|
+
}
|
|
2044
|
+
catch { }
|
|
2045
|
+
}, 16);
|
|
2046
|
+
}
|
|
2047
|
+
catch { }
|
|
2048
|
+
return;
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
1478
2051
|
if (msg.type === 'ns:angular-update' && typeof msg.version === 'number') {
|
|
1479
2052
|
setGraphVersion(Number(msg.version || getGraphVersion() || 0));
|
|
1480
2053
|
}
|
|
1481
|
-
if (await
|
|
2054
|
+
if (CLIENT_STRATEGY?.handleHotUpdateMessage && (await CLIENT_STRATEGY.handleHotUpdateMessage(msg, { getCore, verbose: VERBOSE, performResetRoot, getOverlay: getHmrOverlayApi }))) {
|
|
1482
2055
|
return;
|
|
1483
2056
|
}
|
|
1484
2057
|
}
|
|
@@ -1512,27 +2085,47 @@ async function handleHmrMessage(ev) {
|
|
|
1512
2085
|
return;
|
|
1513
2086
|
}
|
|
1514
2087
|
if (msg.type === 'ns:css-updates' && Array.isArray(msg.updates)) {
|
|
2088
|
+
// Drive the HMR-applying overlay past the 'received' (5%) frame
|
|
2089
|
+
// that `ns:hmr-pending` set earlier in the cycle. Without this
|
|
2090
|
+
// the overlay sticks at "Preparing update" forever for CSS-only
|
|
2091
|
+
// edits because `handleCssUpdates` is a leaf — there's no
|
|
2092
|
+
// downstream module-evaluation path that would hit the queue's
|
|
2093
|
+
// 'complete' transition.
|
|
2094
|
+
const cssCount = msg.updates.length;
|
|
2095
|
+
try {
|
|
2096
|
+
setUpdateOverlayStage('reimporting', { detail: buildCssApplyingDetail(cssCount) });
|
|
2097
|
+
}
|
|
2098
|
+
catch { }
|
|
1515
2099
|
try {
|
|
1516
2100
|
const origin = msg.origin || getHttpOriginForVite() || deriveHttpOrigin(getHMRWsUrl());
|
|
1517
2101
|
await handleCssUpdates(msg.updates, origin);
|
|
2102
|
+
try {
|
|
2103
|
+
setUpdateOverlayStage('complete', { detail: buildCssAppliedDetail(cssCount) });
|
|
2104
|
+
}
|
|
2105
|
+
catch { }
|
|
1518
2106
|
return;
|
|
1519
2107
|
}
|
|
1520
2108
|
catch (e) {
|
|
1521
2109
|
console.warn('[hmr-client] CSS updates handling failed:', e);
|
|
2110
|
+
try {
|
|
2111
|
+
setUpdateOverlayStage('complete', { detail: 'CSS update failed' });
|
|
2112
|
+
}
|
|
2113
|
+
catch { }
|
|
1522
2114
|
return;
|
|
1523
2115
|
}
|
|
1524
2116
|
}
|
|
1525
2117
|
if (msg.type === 'ns:vue-sfc-registry') {
|
|
1526
|
-
|
|
2118
|
+
CLIENT_STRATEGY?.handleSfcRegistry?.(msg);
|
|
1527
2119
|
return;
|
|
1528
2120
|
}
|
|
1529
2121
|
if (msg.type === 'ns:vue-sfc-registry-update') {
|
|
1530
2122
|
if (typeof msg.version === 'number')
|
|
1531
2123
|
setGraphVersion(msg.version);
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
2124
|
+
// `ns:hmr-pending` already set the overlay to 'received' (5%). The Vue
|
|
2125
|
+
// strategy walks 'evicting' → 'reimporting' → 'rebooting' → 'complete'
|
|
2126
|
+
// around the SFC load + reset so the toast always lands on 'complete'
|
|
2127
|
+
// (or a failure detail) and the auto-hide timer can dismiss it.
|
|
2128
|
+
await CLIENT_STRATEGY?.handleSfcRegistryUpdate?.(msg, getGraphVersion(), { getCore, verbose: VERBOSE, performResetRoot, getOverlay: getHmrOverlayApi });
|
|
1536
2129
|
return;
|
|
1537
2130
|
}
|
|
1538
2131
|
}
|
|
@@ -1552,9 +2145,9 @@ function normalizeComponent(input, nameHint) {
|
|
|
1552
2145
|
}
|
|
1553
2146
|
// If provided a render function, wrap with defineComponent
|
|
1554
2147
|
if (typeof input === 'function') {
|
|
1555
|
-
|
|
1556
|
-
const comp =
|
|
1557
|
-
?
|
|
2148
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
2149
|
+
const comp = getGlobalScope().defineComponent
|
|
2150
|
+
? getGlobalScope().defineComponent({
|
|
1558
2151
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1559
2152
|
render: input,
|
|
1560
2153
|
})
|
|
@@ -1563,9 +2156,9 @@ function normalizeComponent(input, nameHint) {
|
|
|
1563
2156
|
}
|
|
1564
2157
|
// If object has a render function property
|
|
1565
2158
|
if (input?.render && typeof input.render === 'function') {
|
|
1566
|
-
|
|
1567
|
-
const comp =
|
|
1568
|
-
?
|
|
2159
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
2160
|
+
const comp = getGlobalScope().defineComponent
|
|
2161
|
+
? getGlobalScope().defineComponent({
|
|
1569
2162
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1570
2163
|
render: input.render,
|
|
1571
2164
|
})
|
|
@@ -1621,35 +2214,32 @@ async function performResetRoot(newComponent) {
|
|
|
1621
2214
|
if (cachedRoot)
|
|
1622
2215
|
return cachedRoot;
|
|
1623
2216
|
try {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
root = newComponent();
|
|
1634
|
-
}
|
|
1635
|
-
else {
|
|
1636
|
-
root = newComponent;
|
|
1637
|
-
}
|
|
2217
|
+
if (CLIENT_STRATEGY?.createRoot) {
|
|
2218
|
+
cachedRoot = CLIENT_STRATEGY.createRoot(newComponent, state);
|
|
2219
|
+
}
|
|
2220
|
+
else if (TARGET_FLAVOR === 'typescript') {
|
|
2221
|
+
// For TS flavor, treat the component as a factory or direct NS view.
|
|
2222
|
+
let root = null;
|
|
2223
|
+
try {
|
|
2224
|
+
if (typeof newComponent === 'function') {
|
|
2225
|
+
root = newComponent();
|
|
1638
2226
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
2227
|
+
else {
|
|
2228
|
+
root = newComponent;
|
|
1641
2229
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2230
|
+
}
|
|
2231
|
+
catch (e) {
|
|
2232
|
+
console.warn('[hmr-client][ts] root factory invocation failed', e);
|
|
2233
|
+
}
|
|
2234
|
+
cachedRoot = root || {};
|
|
2235
|
+
// Heuristic: if the root "looks" like a Frame, prefer frame semantics
|
|
2236
|
+
try {
|
|
2237
|
+
const name = String(cachedRoot?.constructor?.name || '').replace(/^_+/, '');
|
|
2238
|
+
if (/^Frame(\$\d+)?$/.test(name)) {
|
|
2239
|
+
rootKind = 'frame';
|
|
1649
2240
|
}
|
|
1650
|
-
catch { }
|
|
1651
|
-
break;
|
|
1652
2241
|
}
|
|
2242
|
+
catch { }
|
|
1653
2243
|
}
|
|
1654
2244
|
return cachedRoot;
|
|
1655
2245
|
}
|
|
@@ -1667,7 +2257,7 @@ async function performResetRoot(newComponent) {
|
|
|
1667
2257
|
return factory;
|
|
1668
2258
|
}
|
|
1669
2259
|
// Android readiness before any root changes
|
|
1670
|
-
const App = getCore('Application') ||
|
|
2260
|
+
const App = getCore('Application') || getGlobalScope().Application;
|
|
1671
2261
|
const isAndroid = !!(App && App.android !== undefined);
|
|
1672
2262
|
if (isAndroid) {
|
|
1673
2263
|
const isReady = () => {
|
|
@@ -1769,7 +2359,7 @@ async function performResetRoot(newComponent) {
|
|
|
1769
2359
|
}
|
|
1770
2360
|
catch { }
|
|
1771
2361
|
try {
|
|
1772
|
-
const AppAny = getCore('Application') ||
|
|
2362
|
+
const AppAny = getCore('Application') || getGlobalScope().Application;
|
|
1773
2363
|
isIOS = !!(AppAny && AppAny.ios !== undefined);
|
|
1774
2364
|
}
|
|
1775
2365
|
catch { }
|
|
@@ -1779,7 +2369,7 @@ async function performResetRoot(newComponent) {
|
|
|
1779
2369
|
// - Otherwise (subsequent HMR updates with an authoritative Frame already in place), re-use the
|
|
1780
2370
|
// current app Frame and navigate to the new Page. This avoids a brief flash that can occur
|
|
1781
2371
|
// when swapping the entire root view on Android. The placeholder is never involved here.
|
|
1782
|
-
const gAnyForPolicy =
|
|
2372
|
+
const gAnyForPolicy = getGlobalScope();
|
|
1783
2373
|
const placeholderFrame = (() => {
|
|
1784
2374
|
try {
|
|
1785
2375
|
return gAnyForPolicy.__NS_DEV_PLACEHOLDER_ROOT_VIEW__ || null;
|
|
@@ -1794,7 +2384,14 @@ async function performResetRoot(newComponent) {
|
|
|
1794
2384
|
}
|
|
1795
2385
|
catch { }
|
|
1796
2386
|
const isAuthoritativeFrame = !!existingAppFrame && existingAppFrame !== placeholderFrame;
|
|
1797
|
-
|
|
2387
|
+
// Vue: skip the in-place navigate path. After `app.mount(NSVRoot)` in getRootForVue the
|
|
2388
|
+
// new Page already has a parent (the freshly-constructed NSVRoot), so an attempt to navigate
|
|
2389
|
+
// the existing app Frame to that same Page completes silently without ever rebinding the
|
|
2390
|
+
// page to the Frame — the screen keeps showing the previous render. resetRootView with a
|
|
2391
|
+
// fresh Frame correctly reparents the Page and is the proven path that produces visible
|
|
2392
|
+
// in-place updates for SFC HMR cycles. Non-Vue flavors keep the legacy navigate fast path.
|
|
2393
|
+
const allowNavigateFastPath = CLIENT_STRATEGY?.allowNavigateFastPath ?? true;
|
|
2394
|
+
if (allowNavigateFastPath && !hadPlaceholder && !isFrameRoot && isAuthoritativeFrame && typeof existingAppFrame.navigate === 'function') {
|
|
1798
2395
|
try {
|
|
1799
2396
|
const navEntry = {
|
|
1800
2397
|
create: () => preparedRoot,
|
|
@@ -1813,7 +2410,7 @@ async function performResetRoot(newComponent) {
|
|
|
1813
2410
|
console.log('[hmr-client] full root replacement via resetRootView (placeholder will be discarded)', { isFrameRoot, isIOS, hadPlaceholder });
|
|
1814
2411
|
// Fallback or preferred path: resetRootView with a creator that builds a fresh Frame and navigates to the new Page
|
|
1815
2412
|
try {
|
|
1816
|
-
const App2 = getCore('Application') ||
|
|
2413
|
+
const App2 = getCore('Application') || getGlobalScope().Application;
|
|
1817
2414
|
if (!App2 || typeof App2.resetRootView !== 'function') {
|
|
1818
2415
|
console.warn('[hmr-client] Application.resetRootView unavailable');
|
|
1819
2416
|
return false;
|
|
@@ -1832,7 +2429,7 @@ async function performResetRoot(newComponent) {
|
|
|
1832
2429
|
if (VERBOSE)
|
|
1833
2430
|
console.warn('[hmr-client] iOS Application.window is boolean false; attempting to clear cached window');
|
|
1834
2431
|
try {
|
|
1835
|
-
const g =
|
|
2432
|
+
const g = getGlobalScope();
|
|
1836
2433
|
const reg = g.__nsVendorRegistry;
|
|
1837
2434
|
const req = reg?.get ? g.__nsVendorRequire || g.__nsRequire || g.require : g.__nsRequire || g.require;
|
|
1838
2435
|
let helpers = null;
|
|
@@ -1936,6 +2533,20 @@ export function initHmrClient(opts) {
|
|
|
1936
2533
|
}
|
|
1937
2534
|
g.__NS_HMR_CLIENT_ACTIVE__ = true;
|
|
1938
2535
|
ensureCoreAliasesOnGlobalThis();
|
|
2536
|
+
// XML flavor: record string-module modals from the moment the client is up
|
|
2537
|
+
// so an already-open modal can be re-presented when its files change.
|
|
2538
|
+
// Installed at init (not first-update time) because the wrap can only
|
|
2539
|
+
// observe showModal calls made AFTER it lands. Retried briefly because
|
|
2540
|
+
// getCore('View') may not resolve until the vendor realm finishes booting.
|
|
2541
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
2542
|
+
const tryInstallModalTracking = (attempts) => {
|
|
2543
|
+
ensureModalTracking();
|
|
2544
|
+
if (!modalTrackingInstalled && attempts > 0) {
|
|
2545
|
+
setTimeout(() => tryInstallModalTracking(attempts - 1), 250);
|
|
2546
|
+
}
|
|
2547
|
+
};
|
|
2548
|
+
tryInstallModalTracking(40);
|
|
2549
|
+
}
|
|
1939
2550
|
// Defer WebSocket connection until boot completes to avoid native V8 crashes
|
|
1940
2551
|
// caused by concurrent WebSocket message handling + HTTP fetch during early startup.
|
|
1941
2552
|
// The WebSocket is only needed for HMR updates, not the initial boot sequence.
|
|
@@ -1957,12 +2568,9 @@ export function initHmrClient(opts) {
|
|
|
1957
2568
|
console.log('[hmr-client] deferring WebSocket connection until boot completes');
|
|
1958
2569
|
setTimeout(waitForBoot, 100);
|
|
1959
2570
|
}
|
|
1960
|
-
// Best-effort: install back wrapper even before first remount; original root may be captured later
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
ensureBackWrapperInstalled(performResetRoot, getCore);
|
|
1964
|
-
break;
|
|
1965
|
-
}
|
|
2571
|
+
// Best-effort: install back wrapper even before first remount; original root may be captured later.
|
|
2572
|
+
// Deferred until the dynamically-imported strategy resolves.
|
|
2573
|
+
void CLIENT_STRATEGY_READY.then(() => CLIENT_STRATEGY?.installBackWrapper?.(performResetRoot, getCore));
|
|
1966
2574
|
}
|
|
1967
2575
|
export default function startViteHMR(opts) {
|
|
1968
2576
|
if (VERBOSE)
|