@nativescript/vite 8.0.0-alpha.4 → 8.0.0-alpha.40
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 +787 -211
- 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 +75 -26
- package/hmr/shared/runtime/dev-overlay.js +992 -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,6 +107,79 @@ 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;
|
|
@@ -142,19 +221,19 @@ function markHmrConnectionHealthy() {
|
|
|
142
221
|
hideConnectionOverlay();
|
|
143
222
|
}
|
|
144
223
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
224
|
+
let CLIENT_STRATEGY;
|
|
225
|
+
const CLIENT_STRATEGY_READY = TARGET_FLAVOR === 'vue' || TARGET_FLAVOR === 'angular'
|
|
226
|
+
? import(`../frameworks/${TARGET_FLAVOR}/client/strategy.js`)
|
|
227
|
+
.then((mod) => {
|
|
228
|
+
CLIENT_STRATEGY = mod && mod[`${TARGET_FLAVOR}ClientStrategy`];
|
|
229
|
+
if (VERBOSE)
|
|
230
|
+
console.log('[hmr-client] client strategy loaded for flavor:', TARGET_FLAVOR);
|
|
231
|
+
CLIENT_STRATEGY?.install();
|
|
232
|
+
})
|
|
233
|
+
.catch((err) => {
|
|
234
|
+
console.warn('[hmr-client] failed to load client strategy for', TARGET_FLAVOR, err);
|
|
235
|
+
})
|
|
236
|
+
: Promise.resolve();
|
|
158
237
|
// Track whether we've mounted an initial app root yet in HTTP-only boot
|
|
159
238
|
let initialMounted = !!globalThis.__NS_HMR_BOOT_COMPLETE__;
|
|
160
239
|
// Prevent duplicate initial-mount scheduling across rapid full-graph broadcasts and re-evaluations
|
|
@@ -172,7 +251,7 @@ let processingPromise = null;
|
|
|
172
251
|
// Detect whether the early placeholder root is still active on screen
|
|
173
252
|
function isPlaceholderActive() {
|
|
174
253
|
try {
|
|
175
|
-
const g =
|
|
254
|
+
const g = getGlobalScope();
|
|
176
255
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_VIEW__)
|
|
177
256
|
return true;
|
|
178
257
|
if (g.__NS_DEV_PLACEHOLDER_ROOT_EARLY__)
|
|
@@ -202,7 +281,7 @@ function applyFullGraph(payload) {
|
|
|
202
281
|
// causes a double-mount race (rescue fires at 450ms, then main.ts fires ~1s later,
|
|
203
282
|
// causing a visual flash and leaving the app in an inconsistent state).
|
|
204
283
|
try {
|
|
205
|
-
const g =
|
|
284
|
+
const g = getGlobalScope();
|
|
206
285
|
const bootDone = !!g.__NS_HMR_BOOT_COMPLETE__;
|
|
207
286
|
if (!bootDone && !initialMounted && !initialMounting && !g.__NS_HMR_RESCUE_SCHEDULED__ && TARGET_FLAVOR !== 'typescript') {
|
|
208
287
|
// simple snapshot helpers
|
|
@@ -317,39 +396,7 @@ function applyFullGraph(payload) {
|
|
|
317
396
|
}
|
|
318
397
|
return;
|
|
319
398
|
}
|
|
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
|
-
}
|
|
399
|
+
const candidate = CLIENT_STRATEGY?.selectMountCandidate?.({ graph, appMainEntrySpec: APP_MAIN_ENTRY_SPEC }) ?? null;
|
|
353
400
|
if (!candidate)
|
|
354
401
|
return;
|
|
355
402
|
initialMounting = true;
|
|
@@ -360,10 +407,8 @@ function applyFullGraph(payload) {
|
|
|
360
407
|
(async () => {
|
|
361
408
|
try {
|
|
362
409
|
let comp = null;
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
comp = await loadSfcComponent(candidate, 'initial_mount_rescue');
|
|
366
|
-
break;
|
|
410
|
+
if (CLIENT_STRATEGY?.loadComponentForMount) {
|
|
411
|
+
comp = await CLIENT_STRATEGY.loadComponentForMount(candidate, 'initial_mount_rescue');
|
|
367
412
|
}
|
|
368
413
|
if (!comp)
|
|
369
414
|
return;
|
|
@@ -404,41 +449,11 @@ function applyFullGraph(payload) {
|
|
|
404
449
|
// Only allow initial mount when explicitly enabled. Rely on the app's own main entry start() for the first mount
|
|
405
450
|
// to avoid double-mount races that can cause duplicate navigation logs.
|
|
406
451
|
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
|
-
}
|
|
452
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
453
|
+
// For TS flavor, do not perform client-driven initial mount; rely on Application.run.
|
|
454
|
+
return;
|
|
441
455
|
}
|
|
456
|
+
const candidate = CLIENT_STRATEGY?.selectMountCandidate?.({ graph, appMainEntrySpec: APP_MAIN_ENTRY_SPEC }) ?? null;
|
|
442
457
|
if (candidate) {
|
|
443
458
|
// Mark initial-mount in progress (both module-local and global) BEFORE scheduling async work
|
|
444
459
|
initialMounting = true;
|
|
@@ -452,7 +467,7 @@ function applyFullGraph(payload) {
|
|
|
452
467
|
console.log('[hmr][init] mounting initial root from', candidate, 'flavor=', TARGET_FLAVOR);
|
|
453
468
|
// Android-only: avoid racing entry-runtime reset and Activity bring-up
|
|
454
469
|
try {
|
|
455
|
-
const g =
|
|
470
|
+
const g = getGlobalScope();
|
|
456
471
|
const App = getCore('Application') || g.Application;
|
|
457
472
|
const isAndroid = !!(App && App.android !== undefined);
|
|
458
473
|
if (isAndroid) {
|
|
@@ -483,21 +498,19 @@ function applyFullGraph(payload) {
|
|
|
483
498
|
}
|
|
484
499
|
catch { }
|
|
485
500
|
let comp = null;
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
break;
|
|
501
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
502
|
+
try {
|
|
503
|
+
const url = await requestModuleFromServer(candidate);
|
|
504
|
+
const mod = await import(/* @vite-ignore */ url);
|
|
505
|
+
comp = mod && (mod.default || mod);
|
|
506
|
+
}
|
|
507
|
+
catch (e) {
|
|
508
|
+
if (VERBOSE)
|
|
509
|
+
console.warn('[hmr][init] TS initial mount failed to import', candidate, e);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else if (CLIENT_STRATEGY?.loadComponentForMount) {
|
|
513
|
+
comp = await CLIENT_STRATEGY.loadComponentForMount(candidate, 'initial_mount');
|
|
501
514
|
}
|
|
502
515
|
if (comp) {
|
|
503
516
|
const ok = await performResetRoot(comp);
|
|
@@ -558,11 +571,7 @@ function applyDelta(payload) {
|
|
|
558
571
|
setGraphVersion(payload.newVersion);
|
|
559
572
|
}
|
|
560
573
|
const changed = payload.changed || [];
|
|
561
|
-
|
|
562
|
-
case 'vue':
|
|
563
|
-
recordVuePayloadChanges(changed, getGraphVersion());
|
|
564
|
-
break;
|
|
565
|
-
}
|
|
574
|
+
CLIENT_STRATEGY?.recordPayloadChanges?.(changed, getGraphVersion());
|
|
566
575
|
(payload.changed || []).forEach((m) => {
|
|
567
576
|
if (!m || !m.id)
|
|
568
577
|
return;
|
|
@@ -629,7 +638,7 @@ function applyDelta(payload) {
|
|
|
629
638
|
}
|
|
630
639
|
if (isAppMainEntryId(id)) {
|
|
631
640
|
try {
|
|
632
|
-
const exists =
|
|
641
|
+
const exists = getGlobalScope().require?.(id) || globalThis.__nsGetModuleExports?.(id);
|
|
633
642
|
if (!exists && VERBOSE)
|
|
634
643
|
console.log(`[hmr][delta] skipping unresolved ${APP_MAIN_ENTRY_SPEC} change`);
|
|
635
644
|
if (!exists)
|
|
@@ -645,14 +654,10 @@ function applyDelta(payload) {
|
|
|
645
654
|
processQueue();
|
|
646
655
|
}
|
|
647
656
|
}
|
|
648
|
-
// Deterministic navigation using the current Vue app instance rather than vendor-held rootApp
|
|
657
|
+
// Deterministic navigation using the current Vue app instance rather than vendor-held rootApp.
|
|
649
658
|
function __nsNavigateUsingApp(comp, opts = {}) {
|
|
650
|
-
const g =
|
|
651
|
-
|
|
652
|
-
case 'vue':
|
|
653
|
-
ensureVueGlobals();
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
659
|
+
const g = getGlobalScope();
|
|
660
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
656
661
|
const AppFactory = g.createApp;
|
|
657
662
|
const RootCtor = g.NSVRoot;
|
|
658
663
|
if (typeof AppFactory !== 'function' || typeof RootCtor !== 'function') {
|
|
@@ -675,12 +680,14 @@ function __nsNavigateUsingApp(comp, opts = {}) {
|
|
|
675
680
|
const buildTarget = () => {
|
|
676
681
|
const existingApp = getCurrentApp();
|
|
677
682
|
const baseProvides = (existingApp && existingApp._context && existingApp._context.provides) || {};
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
683
|
+
// Forward `opts.props` as Vue's rootProps so `$navigateTo(Comp, { props: { … } })`
|
|
684
|
+
// reaches the destination component. nativescript-vue's stock `$navigateTo`
|
|
685
|
+
// does the same via `createNativeView(target, options?.props, …)` →
|
|
686
|
+
// `renderer.createApp(component, props)`. Dropping props here would surface
|
|
687
|
+
// at the destination as `[Vue warn]: Missing required prop` and any
|
|
688
|
+
// required-prop component would render with `undefined` bindings.
|
|
689
|
+
const app = AppFactory(normalizeComponent(comp, comp && (comp.__name || comp.name)), opts && opts.props);
|
|
690
|
+
CLIENT_STRATEGY?.onNavAppCreated?.(app);
|
|
684
691
|
try {
|
|
685
692
|
const registry = g.__nsVendorRegistry;
|
|
686
693
|
const req = registry?.get ? g.__nsVendorRequire || g.__nsRequire || g.require : g.__nsRequire || g.require;
|
|
@@ -775,6 +782,184 @@ try {
|
|
|
775
782
|
globalThis.__nsNavigateUsingApp = __nsNavigateUsingApp;
|
|
776
783
|
}
|
|
777
784
|
catch { }
|
|
785
|
+
const openModalRecords = [];
|
|
786
|
+
let modalTrackingInstalled = false;
|
|
787
|
+
/**
|
|
788
|
+
* Map a served/graph module id (e.g. `/app/modal-page.xml`) to its app-root
|
|
789
|
+
* relative path (`modal-page.xml`). Single mapping point — the raw-asset
|
|
790
|
+
* re-registration, page-navigation targets, and modal matching all derive
|
|
791
|
+
* from this; keep them in sync by construction.
|
|
792
|
+
*/
|
|
793
|
+
function toAppRelativePath(id) {
|
|
794
|
+
try {
|
|
795
|
+
const spec = normalizeSpec(id);
|
|
796
|
+
const appVirtual = APP_VIRTUAL_WITH_SLASH.replace(/^\//, '');
|
|
797
|
+
let relPath = spec.startsWith('/') ? spec.slice(1) : spec;
|
|
798
|
+
if (relPath.startsWith(appVirtual))
|
|
799
|
+
relPath = relPath.slice(appVirtual.length);
|
|
800
|
+
return relPath || null;
|
|
801
|
+
}
|
|
802
|
+
catch {
|
|
803
|
+
return null;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/** App-root relative module name (no extension) for page-shaped files, else null. */
|
|
807
|
+
function toAppModuleName(id) {
|
|
808
|
+
const relPath = toAppRelativePath(id);
|
|
809
|
+
if (!relPath || !/\.(xml|ts|js)$/i.test(relPath))
|
|
810
|
+
return null;
|
|
811
|
+
return relPath.replace(/\.(xml|ts|js)$/i, '');
|
|
812
|
+
}
|
|
813
|
+
function ensureModalTracking() {
|
|
814
|
+
if (modalTrackingInstalled)
|
|
815
|
+
return;
|
|
816
|
+
try {
|
|
817
|
+
const View = getCore('View') || getGlobalScope().View;
|
|
818
|
+
const proto = View?.prototype;
|
|
819
|
+
if (!proto || typeof proto.showModal !== 'function')
|
|
820
|
+
return;
|
|
821
|
+
const orig = proto.showModal;
|
|
822
|
+
if (orig.__nsHmrModalTracked) {
|
|
823
|
+
modalTrackingInstalled = true;
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const wrapped = function (...args) {
|
|
827
|
+
const result = orig.apply(this, args);
|
|
828
|
+
try {
|
|
829
|
+
if (typeof args[0] === 'string' && result) {
|
|
830
|
+
// Mirror core's getModalOptions arg shapes: (moduleName, options)
|
|
831
|
+
// or the deprecated positional form.
|
|
832
|
+
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] };
|
|
833
|
+
const moduleName = String(args[0])
|
|
834
|
+
.replace(/^\.\//, '')
|
|
835
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
836
|
+
openModalRecords.push({ moduleName, options, parent: this, modal: result });
|
|
837
|
+
if (VERBOSE)
|
|
838
|
+
console.log('[hmr][modal] tracked open modal', moduleName);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch { }
|
|
842
|
+
return result;
|
|
843
|
+
};
|
|
844
|
+
wrapped.__nsHmrModalTracked = true;
|
|
845
|
+
proto.showModal = wrapped;
|
|
846
|
+
modalTrackingInstalled = true;
|
|
847
|
+
}
|
|
848
|
+
catch (e) {
|
|
849
|
+
if (VERBOSE)
|
|
850
|
+
console.warn('[hmr][modal] tracking install failed', e);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Enumerate the modals that are currently presented AND were opened by module
|
|
855
|
+
* name, with everything needed to re-present them.
|
|
856
|
+
*
|
|
857
|
+
* Source of truth is core's live modal stack (`_getRootModalViews()`):
|
|
858
|
+
* - `modal._moduleName` — set by the Builder on every createViewFromEntry
|
|
859
|
+
* view (longstanding, used by livesync), so available on any core.
|
|
860
|
+
* - `modal._modalOptions` — the original ShowModalOptions, stored by core's
|
|
861
|
+
* `_showNativeModalView` (newer cores). For older cores the showModal
|
|
862
|
+
* wrap's records (see ensureModalTracking) fill the gap.
|
|
863
|
+
* - `modal._modalParent` — the presenting view.
|
|
864
|
+
* Stale wrap records are pruned against the live stack while we're here.
|
|
865
|
+
*/
|
|
866
|
+
function getOpenStringModuleModals() {
|
|
867
|
+
const out = [];
|
|
868
|
+
try {
|
|
869
|
+
const App = getCore('Application');
|
|
870
|
+
const root = App?.getRootView?.() || App?._rootView;
|
|
871
|
+
const stack = root?._getRootModalViews?.() || [];
|
|
872
|
+
// Prune wrap records whose modal is gone (keeps the fallback list small).
|
|
873
|
+
for (let i = openModalRecords.length - 1; i >= 0; i--) {
|
|
874
|
+
if (!stack.includes(openModalRecords[i].modal)) {
|
|
875
|
+
openModalRecords.splice(i, 1);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
for (const modal of stack) {
|
|
879
|
+
const record = openModalRecords.find((r) => r.modal === modal);
|
|
880
|
+
const rawModuleName = typeof modal?._moduleName === 'string' && modal._moduleName ? modal._moduleName : record?.moduleName;
|
|
881
|
+
const parent = modal?._modalParent || record?.parent;
|
|
882
|
+
const options = modal?._modalOptions || record?.options;
|
|
883
|
+
if (!rawModuleName || !parent)
|
|
884
|
+
continue;
|
|
885
|
+
const moduleName = String(rawModuleName)
|
|
886
|
+
.replace(/^\.\//, '')
|
|
887
|
+
.replace(/\.(xml|ts|js)$/i, '');
|
|
888
|
+
out.push({ moduleName, options, parent, modal });
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
catch (e) {
|
|
892
|
+
if (VERBOSE)
|
|
893
|
+
console.warn('[hmr][modal] open-modal enumeration failed', e);
|
|
894
|
+
}
|
|
895
|
+
return out;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Close and re-present an open modal so it rebuilds from the freshly
|
|
899
|
+
* re-registered XML/code-behind. Core clears the modal stack synchronously on
|
|
900
|
+
* close but the NATIVE dismissal completes asynchronously; iOS refuses a
|
|
901
|
+
* present while a dismissal is in flight. Newer cores fire `closedModally` on
|
|
902
|
+
* the modal at exactly that completion point — preferred signal. Older cores
|
|
903
|
+
* fall back to polling `isLoaded` (flipped by `_tearDownUI` in the same
|
|
904
|
+
* completion callback).
|
|
905
|
+
*/
|
|
906
|
+
async function reshowOpenModal(record) {
|
|
907
|
+
const { parent, modal, moduleName, options } = record;
|
|
908
|
+
await new Promise((resolve) => {
|
|
909
|
+
let settled = false;
|
|
910
|
+
const finish = () => {
|
|
911
|
+
if (!settled) {
|
|
912
|
+
settled = true;
|
|
913
|
+
resolve();
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
let eventArmed = false;
|
|
917
|
+
try {
|
|
918
|
+
if (typeof modal.once === 'function') {
|
|
919
|
+
modal.once('closedModally', finish);
|
|
920
|
+
eventArmed = true;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
catch { }
|
|
924
|
+
// Poll fallback (also the safety net if the event never fires —
|
|
925
|
+
// e.g. an interactive-dismiss cancellation).
|
|
926
|
+
const deadline = Date.now() + 2000;
|
|
927
|
+
const poll = () => {
|
|
928
|
+
if (settled)
|
|
929
|
+
return;
|
|
930
|
+
let stillLoaded = false;
|
|
931
|
+
try {
|
|
932
|
+
stillLoaded = !!modal.isLoaded;
|
|
933
|
+
}
|
|
934
|
+
catch { }
|
|
935
|
+
if ((!eventArmed && !stillLoaded) || Date.now() > deadline) {
|
|
936
|
+
finish();
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
setTimeout(poll, 50);
|
|
940
|
+
};
|
|
941
|
+
setTimeout(poll, 50);
|
|
942
|
+
try {
|
|
943
|
+
modal.closeModal();
|
|
944
|
+
}
|
|
945
|
+
catch (e) {
|
|
946
|
+
if (VERBOSE)
|
|
947
|
+
console.warn('[hmr][modal] close failed for', moduleName, e);
|
|
948
|
+
finish();
|
|
949
|
+
}
|
|
950
|
+
});
|
|
951
|
+
// One settle beat so the platform finishes releasing the presentation
|
|
952
|
+
// before the new present begins.
|
|
953
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
954
|
+
try {
|
|
955
|
+
parent.showModal(moduleName, { ...(options || {}), animated: false });
|
|
956
|
+
if (VERBOSE)
|
|
957
|
+
console.log('[hmr][modal] re-presented', moduleName);
|
|
958
|
+
}
|
|
959
|
+
catch (e) {
|
|
960
|
+
console.warn('[hmr][modal] re-present failed for', moduleName, e);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
778
963
|
async function processQueue() {
|
|
779
964
|
if (!globalThis.__NS_HMR_BOOT_COMPLETE__) {
|
|
780
965
|
if (VERBOSE)
|
|
@@ -810,7 +995,43 @@ async function processQueue() {
|
|
|
810
995
|
return;
|
|
811
996
|
if (VERBOSE)
|
|
812
997
|
console.log('[hmr][queue] processing changed ids', drained);
|
|
998
|
+
// Track wall-clock so the 'complete' frame can show a meaningful
|
|
999
|
+
// total. Only the Solid + TypeScript flavors drive the overlay
|
|
1000
|
+
// from here; Angular has its own flow inside
|
|
1001
|
+
// `frameworks/angular/client/index.ts`.
|
|
1002
|
+
const tQueueStart = Date.now();
|
|
1003
|
+
const driveSolidOverlay = TARGET_FLAVOR === 'solid';
|
|
1004
|
+
// Explicit eviction step.
|
|
1005
|
+
//
|
|
1006
|
+
// On modern runtimes the URL canonicalizer collapses any
|
|
1007
|
+
// `__ns_hmr__/<tag>/` segment back to a stable cache key, so
|
|
1008
|
+
// without explicit eviction the upcoming `import(url)` would
|
|
1009
|
+
// resolve via V8's `g_moduleRegistry` and return the cached
|
|
1010
|
+
// stale module — making the queue drain a silent no-op for
|
|
1011
|
+
// every save after the first.
|
|
1012
|
+
//
|
|
1013
|
+
// We hand the canonical eviction URLs to the runtime first;
|
|
1014
|
+
// `invalidateModulesByUrls` is a no-op on older runtimes and
|
|
1015
|
+
// `requestModuleFromServer` automatically falls back to the
|
|
1016
|
+
// legacy `/ns/m/__ns_hmr__/v<N>/` URL versioning path in that
|
|
1017
|
+
// case. node_modules and virtual specs are filtered out by
|
|
1018
|
+
// `buildEvictionUrls` so vendor modules stay hot.
|
|
1019
|
+
if (driveSolidOverlay) {
|
|
1020
|
+
setUpdateOverlayStage('evicting', {
|
|
1021
|
+
detail: drained.length === 1 ? `Invalidating ${drained[0]}` : `Invalidating ${drained.length} modules`,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
const evictUrls = buildEvictionUrls(drained);
|
|
1025
|
+
const evicted = invalidateModulesByUrls(evictUrls);
|
|
1026
|
+
if (VERBOSE)
|
|
1027
|
+
console.log(`[hmr][queue] eviction count=${evictUrls.length} ok=${evicted}`);
|
|
813
1028
|
// Evaluate changed modules best-effort; failures shouldn't completely break HMR.
|
|
1029
|
+
if (driveSolidOverlay) {
|
|
1030
|
+
setUpdateOverlayStage('reimporting', {
|
|
1031
|
+
detail: drained.length === 1 ? `Re-importing ${drained[0]}` : `Re-importing ${drained.length} modules`,
|
|
1032
|
+
});
|
|
1033
|
+
}
|
|
1034
|
+
let reimportFailures = 0;
|
|
814
1035
|
for (const id of drained) {
|
|
815
1036
|
try {
|
|
816
1037
|
const spec = normalizeSpec(id);
|
|
@@ -820,18 +1041,56 @@ async function processQueue() {
|
|
|
820
1041
|
if (VERBOSE)
|
|
821
1042
|
console.log('[hmr][queue] re-import', { id, spec, url });
|
|
822
1043
|
const mod = await import(/* @vite-ignore */ url);
|
|
1044
|
+
// TS/XML flavor: refresh the bundler module registry with the fresh
|
|
1045
|
+
// exports so Builder.createViewFromEntry / loadModule('<page>')
|
|
1046
|
+
// resolves the NEW code-behind (tap handlers, page events) instead
|
|
1047
|
+
// of the stale module captured in the boot bundle. Without this,
|
|
1048
|
+
// XML re-renders pick up new markup but keep old behavior.
|
|
1049
|
+
if (TARGET_FLAVOR === 'typescript' && mod && /\.(ts|js)$/i.test(id)) {
|
|
1050
|
+
try {
|
|
1051
|
+
const g = getGlobalScope();
|
|
1052
|
+
const moduleName = toAppModuleName(id);
|
|
1053
|
+
if (moduleName && typeof g.registerModule === 'function') {
|
|
1054
|
+
g.registerModule(moduleName, () => mod);
|
|
1055
|
+
g.registerModule('./' + moduleName, () => mod);
|
|
1056
|
+
if (VERBOSE)
|
|
1057
|
+
console.log('[hmr][queue] re-registered code-behind', moduleName);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
catch (e) {
|
|
1061
|
+
if (VERBOSE)
|
|
1062
|
+
console.warn('[hmr][queue] code-behind re-register failed for', id, e);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
823
1065
|
}
|
|
824
1066
|
catch (e) {
|
|
825
|
-
|
|
826
|
-
|
|
1067
|
+
// Never silent: a failed re-import means the device may keep
|
|
1068
|
+
// running the previous module body, so always surface it (not
|
|
1069
|
+
// verbose-gated) — otherwise the overlay reports success while
|
|
1070
|
+
// the app runs stale code.
|
|
1071
|
+
reimportFailures++;
|
|
1072
|
+
console.warn('[hmr][queue] re-import FAILED for', id, '-', e?.message ?? e);
|
|
827
1073
|
}
|
|
828
1074
|
}
|
|
1075
|
+
if (reimportFailures > 0) {
|
|
1076
|
+
console.warn(`[hmr][queue] ${reimportFailures}/${drained.length} module(s) failed to re-import; the applied update may be incomplete.`);
|
|
1077
|
+
}
|
|
829
1078
|
// After evaluating the batch, perform flavor-specific UI refresh.
|
|
830
1079
|
switch (TARGET_FLAVOR) {
|
|
831
1080
|
case 'vue':
|
|
832
|
-
//
|
|
1081
|
+
// graph + performResetRoot + getOverlay let the Vue strategy
|
|
1082
|
+
// propagate non-SFC dep changes to the nearest `.vue` boundary
|
|
1083
|
+
// and remount it (see `propagateDepChangeToSfcBoundary`).
|
|
1084
|
+
await CLIENT_STRATEGY?.refreshAfterBatch?.(drained, { setUpdateOverlayStage, startedAt: tQueueStart, graph, performResetRoot, getOverlay: getHmrOverlayApi });
|
|
833
1085
|
break;
|
|
834
1086
|
case 'solid': {
|
|
1087
|
+
// Boundaries discovered in this HMR cycle (tsx files reachable
|
|
1088
|
+
// via the reverse import graph from any changed file, plus route
|
|
1089
|
+
// files reachable from any tsx start point). Declared at the top
|
|
1090
|
+
// of the case block so the emit step below can include the
|
|
1091
|
+
// complete set in the listener event — framework integrations
|
|
1092
|
+
// use it to map route boundaries → fresh component references.
|
|
1093
|
+
const boundaries = new Set();
|
|
835
1094
|
// Solid .tsx components are self-accepting via solid-refresh's inline
|
|
836
1095
|
// patchRegistry — re-importing them is sufficient. For non-component
|
|
837
1096
|
// .ts utility modules, we must propagate up the import graph to find
|
|
@@ -850,8 +1109,10 @@ async function processQueue() {
|
|
|
850
1109
|
arr.push(id);
|
|
851
1110
|
}
|
|
852
1111
|
}
|
|
853
|
-
// BFS from each non-tsx changed module up to tsx/jsx
|
|
854
|
-
|
|
1112
|
+
// Pass 1: BFS from each non-tsx changed module up to tsx/jsx
|
|
1113
|
+
// boundaries. These get re-imported below so solid-refresh's
|
|
1114
|
+
// inline patchRegistry runs and (best-effort) swaps the proxy
|
|
1115
|
+
// signals for any components defined in those tsx boundaries.
|
|
855
1116
|
for (const id of drained) {
|
|
856
1117
|
if (/\.(tsx|jsx)$/i.test(id))
|
|
857
1118
|
continue; // already self-accepting
|
|
@@ -875,6 +1136,51 @@ async function processQueue() {
|
|
|
875
1136
|
}
|
|
876
1137
|
}
|
|
877
1138
|
}
|
|
1139
|
+
// Pass 2: walk further from any tsx starting point (a tsx file
|
|
1140
|
+
// in `drained` OR a tsx boundary discovered in pass 1) to find
|
|
1141
|
+
// route files (`/src/routes/*.{tsx,jsx}`) that transitively
|
|
1142
|
+
// import them. Re-importing a route file refreshes its
|
|
1143
|
+
// `Route.options.component` to the freshly-imported reference
|
|
1144
|
+
// and the existing boundary loop below patches the live router
|
|
1145
|
+
// with that fresh reference.
|
|
1146
|
+
//
|
|
1147
|
+
// This is the key fix for "edit home.tsx → save → no visual
|
|
1148
|
+
// update": the old BFS skipped tsx files in `drained` (assuming
|
|
1149
|
+
// solid-refresh's in-place proxy patch was sufficient), but in
|
|
1150
|
+
// the universal-renderer + nested-context configuration that
|
|
1151
|
+
// patch does not always propagate to the visible page tree.
|
|
1152
|
+
// Adding the route file as a boundary lets us patch
|
|
1153
|
+
// `route.options.component` directly to a fresh module export,
|
|
1154
|
+
// which the framework subscriber then passes through to the
|
|
1155
|
+
// page remount — making the cycle robust to the proxy patch
|
|
1156
|
+
// silently failing.
|
|
1157
|
+
const tsxStarts = new Set();
|
|
1158
|
+
for (const id of drained) {
|
|
1159
|
+
if (/\.(tsx|jsx)$/i.test(id))
|
|
1160
|
+
tsxStarts.add(id);
|
|
1161
|
+
}
|
|
1162
|
+
for (const b of boundaries)
|
|
1163
|
+
tsxStarts.add(b);
|
|
1164
|
+
const ROUTE_FILE_RE = /\/src\/routes\/.+\.(tsx|jsx)$/i;
|
|
1165
|
+
for (const start of tsxStarts) {
|
|
1166
|
+
const visited = new Set();
|
|
1167
|
+
const queue = [start];
|
|
1168
|
+
while (queue.length) {
|
|
1169
|
+
const cur = queue.shift();
|
|
1170
|
+
if (visited.has(cur))
|
|
1171
|
+
continue;
|
|
1172
|
+
visited.add(cur);
|
|
1173
|
+
if (cur !== start && ROUTE_FILE_RE.test(cur)) {
|
|
1174
|
+
boundaries.add(cur);
|
|
1175
|
+
}
|
|
1176
|
+
const importers = reverseIndex.get(cur);
|
|
1177
|
+
if (!importers)
|
|
1178
|
+
continue;
|
|
1179
|
+
for (const imp of importers) {
|
|
1180
|
+
queue.push(imp);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
878
1184
|
// Re-import each boundary so solid-refresh patchRegistry fires.
|
|
879
1185
|
// For route files (TanStack Router), capture the new Route export
|
|
880
1186
|
// and patch the router's existing route with the fresh loader.
|
|
@@ -885,7 +1191,7 @@ async function processQueue() {
|
|
|
885
1191
|
const findRouter = () => {
|
|
886
1192
|
if (discoveredRouter)
|
|
887
1193
|
return discoveredRouter;
|
|
888
|
-
const g =
|
|
1194
|
+
const g = getGlobalScope();
|
|
889
1195
|
if (g.__ns_router?.routesById)
|
|
890
1196
|
return (discoveredRouter = g.__ns_router);
|
|
891
1197
|
// Fallback: scan common global keys for router
|
|
@@ -924,6 +1230,15 @@ async function processQueue() {
|
|
|
924
1230
|
}
|
|
925
1231
|
return null;
|
|
926
1232
|
};
|
|
1233
|
+
// Evict the boundary set so re-importing each .tsx
|
|
1234
|
+
// component actually picks up the new transitive
|
|
1235
|
+
// dependency code; without this V8 returns the
|
|
1236
|
+
// cached boundary module unchanged.
|
|
1237
|
+
const boundaryIds = Array.from(boundaries);
|
|
1238
|
+
const solidEvictUrls = buildEvictionUrls(boundaryIds);
|
|
1239
|
+
const solidEvicted = invalidateModulesByUrls(solidEvictUrls);
|
|
1240
|
+
if (VERBOSE)
|
|
1241
|
+
console.log(`[hmr][solid] eviction count=${solidEvictUrls.length} ok=${solidEvicted}`);
|
|
927
1242
|
for (const id of boundaries) {
|
|
928
1243
|
if (seen.has(id))
|
|
929
1244
|
continue;
|
|
@@ -935,22 +1250,28 @@ async function processQueue() {
|
|
|
935
1250
|
if (VERBOSE)
|
|
936
1251
|
console.log('[hmr][solid] propagated to boundary', { id, url });
|
|
937
1252
|
const mod = await import(/* @vite-ignore */ url);
|
|
938
|
-
// Patch TanStack Router route
|
|
1253
|
+
// Patch TanStack Router route options for any module
|
|
1254
|
+
// that exports a `Route`. We patch BOTH the component
|
|
1255
|
+
// and the loader (when present); components-only routes
|
|
1256
|
+
// were previously skipped because the gate required a
|
|
1257
|
+
// loader, which left their `options.component` pointing
|
|
1258
|
+
// at the stale module's exports after HMR.
|
|
939
1259
|
try {
|
|
940
1260
|
const newRoute = mod?.Route;
|
|
941
|
-
if (newRoute?.options
|
|
1261
|
+
if (newRoute?.options) {
|
|
942
1262
|
const router = findRouter();
|
|
943
1263
|
const fullPath = boundaryToFullPath(id);
|
|
944
1264
|
if (VERBOSE)
|
|
945
|
-
console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router,
|
|
1265
|
+
console.log('[hmr][solid][diag] route patch attempt', { id, fullPath, hasRouter: !!router, hasLoader: !!newRoute.options.loader, hasComponent: !!newRoute.options.component });
|
|
946
1266
|
const existingRoute = fullPath && router ? findRouteByFullPath(router, fullPath) : null;
|
|
947
1267
|
if (existingRoute?.options) {
|
|
948
|
-
|
|
1268
|
+
if (newRoute.options.loader)
|
|
1269
|
+
existingRoute.options.loader = newRoute.options.loader;
|
|
949
1270
|
if (newRoute.options.component)
|
|
950
1271
|
existingRoute.options.component = newRoute.options.component;
|
|
951
1272
|
routesPatchCount++;
|
|
952
1273
|
if (VERBOSE)
|
|
953
|
-
console.log('[hmr][solid] patched route
|
|
1274
|
+
console.log('[hmr][solid] patched route', existingRoute.id, 'fullPath=', fullPath);
|
|
954
1275
|
}
|
|
955
1276
|
else if (VERBOSE) {
|
|
956
1277
|
console.log('[hmr][solid] no matching route for fullPath', fullPath);
|
|
@@ -986,6 +1307,44 @@ async function processQueue() {
|
|
|
986
1307
|
if (VERBOSE)
|
|
987
1308
|
console.warn('[hmr][solid] propagation failed', e);
|
|
988
1309
|
}
|
|
1310
|
+
// Notify any framework integrations (e.g.
|
|
1311
|
+
// `@nativescript/tanstack-router`) that a Solid HMR
|
|
1312
|
+
// cycle has completed. They use this signal to perform
|
|
1313
|
+
// framework-specific UI refresh (e.g. remount the active
|
|
1314
|
+
// router page) when solid-refresh's own reactive
|
|
1315
|
+
// propagation does not reach the visible tree under
|
|
1316
|
+
// the current renderer/context configuration.
|
|
1317
|
+
//
|
|
1318
|
+
// Boundaries include both the directly-changed tsx files
|
|
1319
|
+
// AND every tsx ancestor reachable via the reverse import
|
|
1320
|
+
// graph (route files in particular). The framework
|
|
1321
|
+
// listener uses the route-file boundaries to look up the
|
|
1322
|
+
// freshly-patched `route.options.component` and pass it
|
|
1323
|
+
// through to the page remount.
|
|
1324
|
+
try {
|
|
1325
|
+
const tsxChangedInDrained = drained.filter((id) => /\.(tsx|jsx)$/i.test(id));
|
|
1326
|
+
const allBoundaries = Array.from(new Set([...tsxChangedInDrained, ...boundaries]));
|
|
1327
|
+
nsSolidHmrEmit({
|
|
1328
|
+
kind: 'solid',
|
|
1329
|
+
changedFiles: drained.slice(),
|
|
1330
|
+
boundaries: allBoundaries,
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
catch (err) {
|
|
1334
|
+
if (VERBOSE)
|
|
1335
|
+
console.warn('[hmr][solid] emit failed', err);
|
|
1336
|
+
}
|
|
1337
|
+
// Tell the overlay the cycle is done. solid-refresh's
|
|
1338
|
+
// inline patchRegistry has already flushed the new
|
|
1339
|
+
// component bodies into the live tree (the `case
|
|
1340
|
+
// 'solid'` block above re-imports each .tsx
|
|
1341
|
+
// boundary), so by the time we get here the user is
|
|
1342
|
+
// already looking at the new render. The 'complete'
|
|
1343
|
+
// frame surfaces the wall-clock total and triggers
|
|
1344
|
+
// the overlay's auto-hide.
|
|
1345
|
+
setUpdateOverlayStage('complete', {
|
|
1346
|
+
detail: `Total ${Math.max(0, Date.now() - tQueueStart)}ms`,
|
|
1347
|
+
});
|
|
989
1348
|
break;
|
|
990
1349
|
}
|
|
991
1350
|
case 'typescript': {
|
|
@@ -993,7 +1352,7 @@ async function processQueue() {
|
|
|
993
1352
|
// This preserves the shell (Frame, ActionBar, etc.) that the app's
|
|
994
1353
|
// own bootstrapping wires up via `Application.run`.
|
|
995
1354
|
try {
|
|
996
|
-
const g =
|
|
1355
|
+
const g = getGlobalScope();
|
|
997
1356
|
const App = getCore('Application') || g.Application;
|
|
998
1357
|
if (!App || typeof App.resetRootView !== 'function') {
|
|
999
1358
|
if (VERBOSE)
|
|
@@ -1019,10 +1378,9 @@ async function processQueue() {
|
|
|
1019
1378
|
const rawContent = await resp.text();
|
|
1020
1379
|
// Register under all nickname variants the module registry uses.
|
|
1021
1380
|
// 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);
|
|
1381
|
+
const relPath = toAppRelativePath(id);
|
|
1382
|
+
if (!relPath)
|
|
1383
|
+
continue;
|
|
1026
1384
|
const nicknames = ['./' + relPath, relPath];
|
|
1027
1385
|
// Also add without extension for CSS
|
|
1028
1386
|
const extIdx = relPath.lastIndexOf('.');
|
|
@@ -1048,21 +1406,24 @@ async function processQueue() {
|
|
|
1048
1406
|
}
|
|
1049
1407
|
}
|
|
1050
1408
|
}
|
|
1409
|
+
// Modal-aware refresh: pages currently PRESENTED AS MODALS must be
|
|
1410
|
+
// closed + re-presented in place — navigating the top frame to a
|
|
1411
|
+
// modal's page would push it as a frame page, and resetRootView
|
|
1412
|
+
// would dismiss the modal entirely. State comes from core's live
|
|
1413
|
+
// modal stack (_moduleName/_modalOptions/_modalParent); the
|
|
1414
|
+
// showModal wrap only backfills options on older cores.
|
|
1415
|
+
ensureModalTracking();
|
|
1416
|
+
const openModals = getOpenStringModuleModals();
|
|
1417
|
+
const changedModuleNames = new Set(drained.map(toAppModuleName).filter(Boolean));
|
|
1418
|
+
const modalsToReshow = openModals.filter((record) => changedModuleNames.has(record.moduleName));
|
|
1419
|
+
const reshowModuleNames = new Set(modalsToReshow.map((record) => record.moduleName));
|
|
1051
1420
|
// Determine if we can navigate in-place to a changed page
|
|
1052
1421
|
// instead of resetting all the way back to app-root.
|
|
1053
1422
|
// This keeps the user on the page they're editing for faster iteration.
|
|
1054
1423
|
const changedXmlPages = drained
|
|
1055
1424
|
.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');
|
|
1425
|
+
.map((id) => toAppModuleName(id))
|
|
1426
|
+
.filter((m) => m && m !== 'app-root' && !reshowModuleNames.has(m));
|
|
1066
1427
|
// Resolve the topmost Frame from the bundled realm.
|
|
1067
1428
|
// Frame.topmost() relies on an internal frameStack array, so we must
|
|
1068
1429
|
// call it on the bundled-realm class. Multiple strategies to find it:
|
|
@@ -1098,7 +1459,7 @@ async function processQueue() {
|
|
|
1098
1459
|
catch { }
|
|
1099
1460
|
}
|
|
1100
1461
|
if (VERBOSE)
|
|
1101
|
-
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame);
|
|
1462
|
+
console.log('[hmr][queue] TS: changedXmlPages=', changedXmlPages, 'topFrame=', !!topFrame, 'modalsToReshow=', modalsToReshow.length);
|
|
1102
1463
|
if (changedXmlPages.length > 0 && topFrame) {
|
|
1103
1464
|
// Navigate the current frame to the changed page directly.
|
|
1104
1465
|
// Use the last changed XML page (most specific).
|
|
@@ -1113,15 +1474,32 @@ async function processQueue() {
|
|
|
1113
1474
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1114
1475
|
}
|
|
1115
1476
|
}
|
|
1116
|
-
else {
|
|
1477
|
+
else if (modalsToReshow.length === 0) {
|
|
1478
|
+
// No frame page to refresh and no open modal owns the change —
|
|
1479
|
+
// fall back to a full root reset. (Skipped when an open modal is
|
|
1480
|
+
// being re-presented below: resetRootView would dismiss it.)
|
|
1117
1481
|
if (VERBOSE)
|
|
1118
1482
|
console.log('[hmr][queue] TS flavor: resetRootView(app-root) after changes');
|
|
1119
1483
|
App.resetRootView({ moduleName: 'app-root' });
|
|
1120
1484
|
}
|
|
1485
|
+
// Re-present any open modals whose XML/code-behind changed. The
|
|
1486
|
+
// modules were already re-registered above (raw XML assets + fresh
|
|
1487
|
+
// code-behind exports), so the re-presented modal rebuilds from
|
|
1488
|
+
// the new content while the page beneath it stays put.
|
|
1489
|
+
for (const record of modalsToReshow) {
|
|
1490
|
+
await reshowOpenModal(record);
|
|
1491
|
+
}
|
|
1121
1492
|
}
|
|
1122
1493
|
catch (e) {
|
|
1123
1494
|
console.warn('[hmr][queue] TS flavor: resetRootView(app-root) failed', e);
|
|
1124
1495
|
}
|
|
1496
|
+
// Tell the overlay the cycle is done — same as the solid path
|
|
1497
|
+
// above. Without this the applying overlay sticks at
|
|
1498
|
+
// 'received' (5%) forever even though the in-place navigate /
|
|
1499
|
+
// resetRootView already applied the update.
|
|
1500
|
+
setUpdateOverlayStage('complete', {
|
|
1501
|
+
detail: `Total ${Math.max(0, Date.now() - tQueueStart)}ms`,
|
|
1502
|
+
});
|
|
1125
1503
|
break;
|
|
1126
1504
|
}
|
|
1127
1505
|
}
|
|
@@ -1129,11 +1507,37 @@ async function processQueue() {
|
|
|
1129
1507
|
finally {
|
|
1130
1508
|
processingQueue = false;
|
|
1131
1509
|
processingPromise = null;
|
|
1510
|
+
// If a delta arrived mid-cycle (pushed onto changedQueue while
|
|
1511
|
+
// processingQueue was still true, so its processQueue() call early-
|
|
1512
|
+
// returned), re-drain — otherwise that save is stranded until the next
|
|
1513
|
+
// delta. Deferred via setTimeout to avoid synchronous re-entrancy as
|
|
1514
|
+
// this promise settles.
|
|
1515
|
+
if (changedQueue.length) {
|
|
1516
|
+
setTimeout(() => {
|
|
1517
|
+
try {
|
|
1518
|
+
processQueue();
|
|
1519
|
+
}
|
|
1520
|
+
catch { }
|
|
1521
|
+
}, 0);
|
|
1522
|
+
}
|
|
1132
1523
|
}
|
|
1133
1524
|
})();
|
|
1134
1525
|
return processingPromise;
|
|
1135
1526
|
}
|
|
1136
1527
|
let hmrSocket = null;
|
|
1528
|
+
// Single reconnect timer. Overlapping close/timeout events used to each schedule
|
|
1529
|
+
// their own `setTimeout(connectHmr, …)`, stacking multiple pending reconnects
|
|
1530
|
+
// that could spawn (and leak) duplicate sockets/listeners. Route all reconnect
|
|
1531
|
+
// scheduling through here so only one is ever pending.
|
|
1532
|
+
let reconnectTimer = null;
|
|
1533
|
+
function scheduleReconnect(delayMs) {
|
|
1534
|
+
if (reconnectTimer)
|
|
1535
|
+
clearTimeout(reconnectTimer);
|
|
1536
|
+
reconnectTimer = setTimeout(() => {
|
|
1537
|
+
reconnectTimer = null;
|
|
1538
|
+
connectHmr();
|
|
1539
|
+
}, delayMs);
|
|
1540
|
+
}
|
|
1137
1541
|
// Track server-announced batches for each version so we can import in-order client-side
|
|
1138
1542
|
const txnClientBatches = new Map();
|
|
1139
1543
|
// Public hook for NativeScript runtime to call from ImportModuleDynamicallyCallback later.
|
|
@@ -1161,10 +1565,16 @@ function connectHmr() {
|
|
|
1161
1565
|
if (hmrSocket?.readyState === WebSocket.OPEN)
|
|
1162
1566
|
return;
|
|
1163
1567
|
if (hmrSocket?.readyState === WebSocket.CONNECTING) {
|
|
1164
|
-
if (
|
|
1568
|
+
if (VERBOSE)
|
|
1165
1569
|
console.log('[hmr-client] Already connecting to HMR WebSocket, skipping');
|
|
1166
1570
|
return;
|
|
1167
1571
|
}
|
|
1572
|
+
// A reconnect fired (or a manual connect raced one) — cancel any other pending
|
|
1573
|
+
// reconnect so we don't end up with overlapping connect attempts.
|
|
1574
|
+
if (reconnectTimer) {
|
|
1575
|
+
clearTimeout(reconnectTimer);
|
|
1576
|
+
reconnectTimer = null;
|
|
1577
|
+
}
|
|
1168
1578
|
try {
|
|
1169
1579
|
globalThis.__NS_HMR_CLIENT_SOCKET_READY__ = false;
|
|
1170
1580
|
}
|
|
@@ -1172,7 +1582,7 @@ function connectHmr() {
|
|
|
1172
1582
|
const overlayStage = hasOpenedHmrSocket ? 'reconnecting' : 'connecting';
|
|
1173
1583
|
const baseUrl = getHMRWsUrl() || 'ws://localhost:5173/ns-hmr';
|
|
1174
1584
|
const buildCandidates = (url) => {
|
|
1175
|
-
|
|
1585
|
+
const candidates = [];
|
|
1176
1586
|
try {
|
|
1177
1587
|
const u = new URL(url);
|
|
1178
1588
|
const proto = u.protocol === 'wss:' ? ['wss'] : ['ws'];
|
|
@@ -1180,7 +1590,7 @@ function connectHmr() {
|
|
|
1180
1590
|
// Build ordered host candidates with preference to the active HTTP origin
|
|
1181
1591
|
const orderedHosts = [];
|
|
1182
1592
|
try {
|
|
1183
|
-
const g =
|
|
1593
|
+
const g = getGlobalScope();
|
|
1184
1594
|
const httpOrigin = g && typeof g.__NS_HTTP_ORIGIN__ === 'string' ? g.__NS_HTTP_ORIGIN__ : undefined;
|
|
1185
1595
|
if (httpOrigin) {
|
|
1186
1596
|
try {
|
|
@@ -1231,7 +1641,7 @@ function connectHmr() {
|
|
|
1231
1641
|
if (idx >= candidates.length) {
|
|
1232
1642
|
showConnectionOverlayNow('offline', 'Waiting for the Vite websocket to come back.');
|
|
1233
1643
|
console.warn('[hmr-client] All WS candidates failed:', candidates.join(', '));
|
|
1234
|
-
|
|
1644
|
+
scheduleReconnect(1500);
|
|
1235
1645
|
return;
|
|
1236
1646
|
}
|
|
1237
1647
|
const url = candidates[idx++];
|
|
@@ -1243,7 +1653,7 @@ function connectHmr() {
|
|
|
1243
1653
|
scheduleConnectionOverlay(overlayStage, connectionDetail);
|
|
1244
1654
|
}
|
|
1245
1655
|
try {
|
|
1246
|
-
if (
|
|
1656
|
+
if (VERBOSE)
|
|
1247
1657
|
console.log('[hmr-client] Connecting to HMR WebSocket:', url);
|
|
1248
1658
|
const sock = new WebSocket(url);
|
|
1249
1659
|
hmrSocket = sock;
|
|
@@ -1273,7 +1683,16 @@ function connectHmr() {
|
|
|
1273
1683
|
if (connectionOverlayVisible) {
|
|
1274
1684
|
showConnectionOverlayNow('synchronizing', 'Connected. Synchronizing the HMR graph.');
|
|
1275
1685
|
}
|
|
1276
|
-
|
|
1686
|
+
if (VERBOSE)
|
|
1687
|
+
console.log('[hmr-client] Connected to HMR WebSocket');
|
|
1688
|
+
// Print the active module reload mode once on first
|
|
1689
|
+
// successful connect so the user can correlate HMR latency
|
|
1690
|
+
// with runtime capability without grepping for protocol
|
|
1691
|
+
// details. The banner is verbose-gated.
|
|
1692
|
+
try {
|
|
1693
|
+
emitHmrModeBannerOnce();
|
|
1694
|
+
}
|
|
1695
|
+
catch { }
|
|
1277
1696
|
};
|
|
1278
1697
|
sock.onmessage = handleHmrMessage;
|
|
1279
1698
|
sock.onerror = (error) => {
|
|
@@ -1297,7 +1716,7 @@ function connectHmr() {
|
|
|
1297
1716
|
console.log('[hmr-client] WebSocket closed (code', ev?.code, '), will reconnect…');
|
|
1298
1717
|
scheduleConnectionOverlay('reconnecting', 'The websocket closed. Waiting to reconnect.', 700);
|
|
1299
1718
|
// try to reconnect with full candidate list again
|
|
1300
|
-
|
|
1719
|
+
scheduleReconnect(1000);
|
|
1301
1720
|
}
|
|
1302
1721
|
};
|
|
1303
1722
|
}
|
|
@@ -1333,10 +1752,26 @@ async function handleHmrMessage(ev) {
|
|
|
1333
1752
|
catch { }
|
|
1334
1753
|
}
|
|
1335
1754
|
if (msg) {
|
|
1755
|
+
// `ns:hmr-pending` is a fire-and-forget UX hint emitted by the
|
|
1756
|
+
// server at the START of handleHotUpdate. We drive the
|
|
1757
|
+
// HMR-applying overlay's 'received' frame here (synchronously),
|
|
1758
|
+
// well before the authoritative payload (`ns:angular-update` /
|
|
1759
|
+
// `ns:css-updates`) lands. Skip running any other handlers —
|
|
1760
|
+
// the pending message has no module payload and intentionally
|
|
1761
|
+
// does not bump the graph version.
|
|
1762
|
+
if (msg.type === 'ns:hmr-pending' && typeof msg.path === 'string') {
|
|
1763
|
+
setHmrPendingOverlay(msg.path);
|
|
1764
|
+
return;
|
|
1765
|
+
}
|
|
1766
|
+
// The per-flavor client strategy is loaded by a dynamic import(); make
|
|
1767
|
+
// sure it has resolved (and `install()` has run) before any handler that
|
|
1768
|
+
// delegates through it. After the first message this is an already-settled
|
|
1769
|
+
// promise (microtask only); for Solid/TypeScript it is `Promise.resolve()`.
|
|
1770
|
+
await CLIENT_STRATEGY_READY;
|
|
1336
1771
|
if (msg.type === 'ns:hmr-full-graph') {
|
|
1337
1772
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1338
1773
|
try {
|
|
1339
|
-
const g =
|
|
1774
|
+
const g = getGlobalScope();
|
|
1340
1775
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1341
1776
|
}
|
|
1342
1777
|
catch { }
|
|
@@ -1402,6 +1837,14 @@ async function handleHmrMessage(ev) {
|
|
|
1402
1837
|
});
|
|
1403
1838
|
if (toReimport.length && VERBOSE)
|
|
1404
1839
|
console.log('[hmr][full-graph] inferred changed modules; re-importing', toReimport);
|
|
1840
|
+
// Evict the inferred changed set before re-importing.
|
|
1841
|
+
// See `processQueue` for the architectural rationale; the
|
|
1842
|
+
// full-graph code path is the resync fallback (server chose
|
|
1843
|
+
// not to send a delta) and shares the same V8 cache pitfall.
|
|
1844
|
+
const fgEvictUrls = buildEvictionUrls(toReimport);
|
|
1845
|
+
const fgEvicted = invalidateModulesByUrls(fgEvictUrls);
|
|
1846
|
+
if (VERBOSE)
|
|
1847
|
+
console.log(`[hmr][full-graph] eviction count=${fgEvictUrls.length} ok=${fgEvicted}`);
|
|
1405
1848
|
for (const id of toReimport) {
|
|
1406
1849
|
try {
|
|
1407
1850
|
const spec = normalizeSpec(id);
|
|
@@ -1453,7 +1896,7 @@ async function handleHmrMessage(ev) {
|
|
|
1453
1896
|
if (msg.type === 'ns:hmr-delta') {
|
|
1454
1897
|
// Bump a monotonic nonce so HTTP ESM imports can always be cache-busted per update.
|
|
1455
1898
|
try {
|
|
1456
|
-
const g =
|
|
1899
|
+
const g = getGlobalScope();
|
|
1457
1900
|
g.__NS_HMR_IMPORT_NONCE__ = (typeof g.__NS_HMR_IMPORT_NONCE__ === 'number' ? g.__NS_HMR_IMPORT_NONCE__ : 0) + 1;
|
|
1458
1901
|
}
|
|
1459
1902
|
catch { }
|
|
@@ -1475,10 +1918,108 @@ async function handleHmrMessage(ev) {
|
|
|
1475
1918
|
return;
|
|
1476
1919
|
}
|
|
1477
1920
|
else {
|
|
1921
|
+
// Vite custom-event dispatch.
|
|
1922
|
+
//
|
|
1923
|
+
// `server.ws.send('event-name', payload)` from any Vite plugin lands
|
|
1924
|
+
// on the wire as `{ type: 'custom', event: 'event-name', data: payload }`.
|
|
1925
|
+
// On the web, Vite's stock client owns a `customListenersMap` that
|
|
1926
|
+
// fires every `import.meta.hot.on('event-name', cb)` callback. We
|
|
1927
|
+
// don't run Vite's stock client on device — the iOS runtime owns
|
|
1928
|
+
// the listener registry via `__NS_DISPATCH_HOT_EVENT__` (the
|
|
1929
|
+
// counterpart to `import.meta.hot.on` populated by user code +
|
|
1930
|
+
// compiled Angular components). Forwarding `type: 'custom'` here
|
|
1931
|
+
// is the only thing standing between server-emitted events and
|
|
1932
|
+
// the listeners they were meant for.
|
|
1933
|
+
//
|
|
1934
|
+
// `angular:component-update` is the canonical example. Analog's
|
|
1935
|
+
// plugin sends it on `.html` / component-style edits; the
|
|
1936
|
+
// compiled component `.mjs` registered a listener that
|
|
1937
|
+
// dynamic-imports `/@ng/component?c=<id>&t=<ts>` and calls
|
|
1938
|
+
// `ɵɵreplaceMetadata` on the live class — swapping the template
|
|
1939
|
+
// definition AND walking live `LView`s to recreate matching views
|
|
1940
|
+
// in-place. The page stays mounted and only the changed bits
|
|
1941
|
+
// re-render. We MUST `return` after dispatch so the reboot path
|
|
1942
|
+
// (`handleAngularHotUpdateMessage` → `__reboot_ng_modules__`)
|
|
1943
|
+
// never runs for these updates — that's the whole point of the
|
|
1944
|
+
// component-replacement pipeline.
|
|
1945
|
+
//
|
|
1946
|
+
// All other custom events are forwarded but NOT short-circuited
|
|
1947
|
+
// (Vite spec: custom events are additive — they don't replace
|
|
1948
|
+
// any framework-specific handling). The reboot path falls through
|
|
1949
|
+
// for `ns:angular-update` (the legacy/`.ts`-edit broadcast) and
|
|
1950
|
+
// for any framework not yet using the in-place replacement path.
|
|
1951
|
+
if (msg.type === 'custom' && typeof msg.event === 'string') {
|
|
1952
|
+
// Dispatch every Vite "custom" event through the runtime's
|
|
1953
|
+
// `__NS_DISPATCH_HOT_EVENT__` bridge so `import.meta.hot.on(event, cb)`
|
|
1954
|
+
// callbacks fire on the device. Critical contract: this is the
|
|
1955
|
+
// ONLY route by which Analog's `angular:component-update` reaches
|
|
1956
|
+
// the compiled component's `(d) => d.id === id && Component_HmrLoad(...)`
|
|
1957
|
+
// listener — without it, server-side broadcasts log green
|
|
1958
|
+
// (`(client) hmr update`) while the device sees nothing happen.
|
|
1959
|
+
//
|
|
1960
|
+
// Diagnostic policy: log "no dispatcher" loud (boot-time rt-bridge
|
|
1961
|
+
// failure), and listener exceptions loud (compiled HmrLoad
|
|
1962
|
+
// fetch/parse error). Successful dispatches are silent — the
|
|
1963
|
+
// runtime's `[import.meta.hot] dispatch summary` line carries
|
|
1964
|
+
// the per-event match-count diagnostic.
|
|
1965
|
+
try {
|
|
1966
|
+
const dispatch = globalThis.__NS_DISPATCH_HOT_EVENT__;
|
|
1967
|
+
if (typeof dispatch === 'function') {
|
|
1968
|
+
dispatch(msg.event, msg.data);
|
|
1969
|
+
}
|
|
1970
|
+
else {
|
|
1971
|
+
console.warn(`[hmr-client][custom] no __NS_DISPATCH_HOT_EVENT__ available for '${msg.event}'`);
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
catch (err) {
|
|
1975
|
+
console.warn('[hmr-client][custom] dispatch threw for', msg.event, err);
|
|
1976
|
+
}
|
|
1977
|
+
if (msg.event === 'angular:component-update') {
|
|
1978
|
+
if (VERBOSE)
|
|
1979
|
+
console.log('[hmr-client][custom] dispatched angular:component-update — skipping reboot path');
|
|
1980
|
+
// Walk the apply-progress overlay through its
|
|
1981
|
+
// remaining stages for the in-place template-swap
|
|
1982
|
+
// path. The full reboot path
|
|
1983
|
+
// (`handleAngularHotUpdateMessage`) drives the
|
|
1984
|
+
// overlay itself ('received' → 'evicting' →
|
|
1985
|
+
// 'reimporting' → 'rebooting' → 'complete'); the
|
|
1986
|
+
// in-place path bypasses that handler entirely
|
|
1987
|
+
// because the work happens inside Angular's
|
|
1988
|
+
// `ɵɵreplaceMetadata` after the runtime forwards the
|
|
1989
|
+
// `angular:component-update` event to the compiled
|
|
1990
|
+
// component's listener. Without this update the
|
|
1991
|
+
// overlay would freeze at 5% ('received') even
|
|
1992
|
+
// though the visual swap completes a few frames
|
|
1993
|
+
// later — exactly the "Preparing update (5%)" stuck
|
|
1994
|
+
// frame we have been chasing.
|
|
1995
|
+
//
|
|
1996
|
+
// We transition straight to 'reimporting' to
|
|
1997
|
+
// communicate that metadata is being fetched (the
|
|
1998
|
+
// runtime listener fires `__ns_import('/@ng/component?c=...&t=...')`),
|
|
1999
|
+
// then schedule 'complete' on the next macrotask so
|
|
2000
|
+
// the auto-hide timer kicks in. The actual
|
|
2001
|
+
// template swap is fire-and-forget from this point;
|
|
2002
|
+
// the user sees the overlay close at the same time
|
|
2003
|
+
// as Angular re-renders the bound text/structure.
|
|
2004
|
+
try {
|
|
2005
|
+
const filePath = typeof msg.data?.id === 'string' ? decodeURIComponent(msg.data.id).split('@')[0] : undefined;
|
|
2006
|
+
const detail = filePath ? `Applying template update to ${filePath}` : 'Applying template update';
|
|
2007
|
+
setUpdateOverlayStage('reimporting', { detail });
|
|
2008
|
+
setTimeout(() => {
|
|
2009
|
+
try {
|
|
2010
|
+
setUpdateOverlayStage('complete', { detail: filePath ? `Updated ${filePath}` : 'Update applied' });
|
|
2011
|
+
}
|
|
2012
|
+
catch { }
|
|
2013
|
+
}, 16);
|
|
2014
|
+
}
|
|
2015
|
+
catch { }
|
|
2016
|
+
return;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
1478
2019
|
if (msg.type === 'ns:angular-update' && typeof msg.version === 'number') {
|
|
1479
2020
|
setGraphVersion(Number(msg.version || getGraphVersion() || 0));
|
|
1480
2021
|
}
|
|
1481
|
-
if (await
|
|
2022
|
+
if (CLIENT_STRATEGY?.handleHotUpdateMessage && (await CLIENT_STRATEGY.handleHotUpdateMessage(msg, { getCore, verbose: VERBOSE, performResetRoot, getOverlay: getHmrOverlayApi }))) {
|
|
1482
2023
|
return;
|
|
1483
2024
|
}
|
|
1484
2025
|
}
|
|
@@ -1512,27 +2053,47 @@ async function handleHmrMessage(ev) {
|
|
|
1512
2053
|
return;
|
|
1513
2054
|
}
|
|
1514
2055
|
if (msg.type === 'ns:css-updates' && Array.isArray(msg.updates)) {
|
|
2056
|
+
// Drive the HMR-applying overlay past the 'received' (5%) frame
|
|
2057
|
+
// that `ns:hmr-pending` set earlier in the cycle. Without this
|
|
2058
|
+
// the overlay sticks at "Preparing update" forever for CSS-only
|
|
2059
|
+
// edits because `handleCssUpdates` is a leaf — there's no
|
|
2060
|
+
// downstream module-evaluation path that would hit the queue's
|
|
2061
|
+
// 'complete' transition.
|
|
2062
|
+
const cssCount = msg.updates.length;
|
|
2063
|
+
try {
|
|
2064
|
+
setUpdateOverlayStage('reimporting', { detail: buildCssApplyingDetail(cssCount) });
|
|
2065
|
+
}
|
|
2066
|
+
catch { }
|
|
1515
2067
|
try {
|
|
1516
2068
|
const origin = msg.origin || getHttpOriginForVite() || deriveHttpOrigin(getHMRWsUrl());
|
|
1517
2069
|
await handleCssUpdates(msg.updates, origin);
|
|
2070
|
+
try {
|
|
2071
|
+
setUpdateOverlayStage('complete', { detail: buildCssAppliedDetail(cssCount) });
|
|
2072
|
+
}
|
|
2073
|
+
catch { }
|
|
1518
2074
|
return;
|
|
1519
2075
|
}
|
|
1520
2076
|
catch (e) {
|
|
1521
2077
|
console.warn('[hmr-client] CSS updates handling failed:', e);
|
|
2078
|
+
try {
|
|
2079
|
+
setUpdateOverlayStage('complete', { detail: 'CSS update failed' });
|
|
2080
|
+
}
|
|
2081
|
+
catch { }
|
|
1522
2082
|
return;
|
|
1523
2083
|
}
|
|
1524
2084
|
}
|
|
1525
2085
|
if (msg.type === 'ns:vue-sfc-registry') {
|
|
1526
|
-
|
|
2086
|
+
CLIENT_STRATEGY?.handleSfcRegistry?.(msg);
|
|
1527
2087
|
return;
|
|
1528
2088
|
}
|
|
1529
2089
|
if (msg.type === 'ns:vue-sfc-registry-update') {
|
|
1530
2090
|
if (typeof msg.version === 'number')
|
|
1531
2091
|
setGraphVersion(msg.version);
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
2092
|
+
// `ns:hmr-pending` already set the overlay to 'received' (5%). The Vue
|
|
2093
|
+
// strategy walks 'evicting' → 'reimporting' → 'rebooting' → 'complete'
|
|
2094
|
+
// around the SFC load + reset so the toast always lands on 'complete'
|
|
2095
|
+
// (or a failure detail) and the auto-hide timer can dismiss it.
|
|
2096
|
+
await CLIENT_STRATEGY?.handleSfcRegistryUpdate?.(msg, getGraphVersion(), { getCore, verbose: VERBOSE, performResetRoot, getOverlay: getHmrOverlayApi });
|
|
1536
2097
|
return;
|
|
1537
2098
|
}
|
|
1538
2099
|
}
|
|
@@ -1552,9 +2113,9 @@ function normalizeComponent(input, nameHint) {
|
|
|
1552
2113
|
}
|
|
1553
2114
|
// If provided a render function, wrap with defineComponent
|
|
1554
2115
|
if (typeof input === 'function') {
|
|
1555
|
-
|
|
1556
|
-
const comp =
|
|
1557
|
-
?
|
|
2116
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
2117
|
+
const comp = getGlobalScope().defineComponent
|
|
2118
|
+
? getGlobalScope().defineComponent({
|
|
1558
2119
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1559
2120
|
render: input,
|
|
1560
2121
|
})
|
|
@@ -1563,9 +2124,9 @@ function normalizeComponent(input, nameHint) {
|
|
|
1563
2124
|
}
|
|
1564
2125
|
// If object has a render function property
|
|
1565
2126
|
if (input?.render && typeof input.render === 'function') {
|
|
1566
|
-
|
|
1567
|
-
const comp =
|
|
1568
|
-
?
|
|
2127
|
+
CLIENT_STRATEGY?.beforeNavigateBuild?.();
|
|
2128
|
+
const comp = getGlobalScope().defineComponent
|
|
2129
|
+
? getGlobalScope().defineComponent({
|
|
1569
2130
|
name: nameHint || input.name || 'AnonymousSFC',
|
|
1570
2131
|
render: input.render,
|
|
1571
2132
|
})
|
|
@@ -1621,35 +2182,32 @@ async function performResetRoot(newComponent) {
|
|
|
1621
2182
|
if (cachedRoot)
|
|
1622
2183
|
return cachedRoot;
|
|
1623
2184
|
try {
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
root = newComponent();
|
|
1634
|
-
}
|
|
1635
|
-
else {
|
|
1636
|
-
root = newComponent;
|
|
1637
|
-
}
|
|
2185
|
+
if (CLIENT_STRATEGY?.createRoot) {
|
|
2186
|
+
cachedRoot = CLIENT_STRATEGY.createRoot(newComponent, state);
|
|
2187
|
+
}
|
|
2188
|
+
else if (TARGET_FLAVOR === 'typescript') {
|
|
2189
|
+
// For TS flavor, treat the component as a factory or direct NS view.
|
|
2190
|
+
let root = null;
|
|
2191
|
+
try {
|
|
2192
|
+
if (typeof newComponent === 'function') {
|
|
2193
|
+
root = newComponent();
|
|
1638
2194
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
2195
|
+
else {
|
|
2196
|
+
root = newComponent;
|
|
1641
2197
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
2198
|
+
}
|
|
2199
|
+
catch (e) {
|
|
2200
|
+
console.warn('[hmr-client][ts] root factory invocation failed', e);
|
|
2201
|
+
}
|
|
2202
|
+
cachedRoot = root || {};
|
|
2203
|
+
// Heuristic: if the root "looks" like a Frame, prefer frame semantics
|
|
2204
|
+
try {
|
|
2205
|
+
const name = String(cachedRoot?.constructor?.name || '').replace(/^_+/, '');
|
|
2206
|
+
if (/^Frame(\$\d+)?$/.test(name)) {
|
|
2207
|
+
rootKind = 'frame';
|
|
1649
2208
|
}
|
|
1650
|
-
catch { }
|
|
1651
|
-
break;
|
|
1652
2209
|
}
|
|
2210
|
+
catch { }
|
|
1653
2211
|
}
|
|
1654
2212
|
return cachedRoot;
|
|
1655
2213
|
}
|
|
@@ -1667,7 +2225,7 @@ async function performResetRoot(newComponent) {
|
|
|
1667
2225
|
return factory;
|
|
1668
2226
|
}
|
|
1669
2227
|
// Android readiness before any root changes
|
|
1670
|
-
const App = getCore('Application') ||
|
|
2228
|
+
const App = getCore('Application') || getGlobalScope().Application;
|
|
1671
2229
|
const isAndroid = !!(App && App.android !== undefined);
|
|
1672
2230
|
if (isAndroid) {
|
|
1673
2231
|
const isReady = () => {
|
|
@@ -1769,7 +2327,7 @@ async function performResetRoot(newComponent) {
|
|
|
1769
2327
|
}
|
|
1770
2328
|
catch { }
|
|
1771
2329
|
try {
|
|
1772
|
-
const AppAny = getCore('Application') ||
|
|
2330
|
+
const AppAny = getCore('Application') || getGlobalScope().Application;
|
|
1773
2331
|
isIOS = !!(AppAny && AppAny.ios !== undefined);
|
|
1774
2332
|
}
|
|
1775
2333
|
catch { }
|
|
@@ -1779,7 +2337,7 @@ async function performResetRoot(newComponent) {
|
|
|
1779
2337
|
// - Otherwise (subsequent HMR updates with an authoritative Frame already in place), re-use the
|
|
1780
2338
|
// current app Frame and navigate to the new Page. This avoids a brief flash that can occur
|
|
1781
2339
|
// when swapping the entire root view on Android. The placeholder is never involved here.
|
|
1782
|
-
const gAnyForPolicy =
|
|
2340
|
+
const gAnyForPolicy = getGlobalScope();
|
|
1783
2341
|
const placeholderFrame = (() => {
|
|
1784
2342
|
try {
|
|
1785
2343
|
return gAnyForPolicy.__NS_DEV_PLACEHOLDER_ROOT_VIEW__ || null;
|
|
@@ -1794,7 +2352,14 @@ async function performResetRoot(newComponent) {
|
|
|
1794
2352
|
}
|
|
1795
2353
|
catch { }
|
|
1796
2354
|
const isAuthoritativeFrame = !!existingAppFrame && existingAppFrame !== placeholderFrame;
|
|
1797
|
-
|
|
2355
|
+
// Vue: skip the in-place navigate path. After `app.mount(NSVRoot)` in getRootForVue the
|
|
2356
|
+
// new Page already has a parent (the freshly-constructed NSVRoot), so an attempt to navigate
|
|
2357
|
+
// the existing app Frame to that same Page completes silently without ever rebinding the
|
|
2358
|
+
// page to the Frame — the screen keeps showing the previous render. resetRootView with a
|
|
2359
|
+
// fresh Frame correctly reparents the Page and is the proven path that produces visible
|
|
2360
|
+
// in-place updates for SFC HMR cycles. Non-Vue flavors keep the legacy navigate fast path.
|
|
2361
|
+
const allowNavigateFastPath = CLIENT_STRATEGY?.allowNavigateFastPath ?? true;
|
|
2362
|
+
if (allowNavigateFastPath && !hadPlaceholder && !isFrameRoot && isAuthoritativeFrame && typeof existingAppFrame.navigate === 'function') {
|
|
1798
2363
|
try {
|
|
1799
2364
|
const navEntry = {
|
|
1800
2365
|
create: () => preparedRoot,
|
|
@@ -1813,7 +2378,7 @@ async function performResetRoot(newComponent) {
|
|
|
1813
2378
|
console.log('[hmr-client] full root replacement via resetRootView (placeholder will be discarded)', { isFrameRoot, isIOS, hadPlaceholder });
|
|
1814
2379
|
// Fallback or preferred path: resetRootView with a creator that builds a fresh Frame and navigates to the new Page
|
|
1815
2380
|
try {
|
|
1816
|
-
const App2 = getCore('Application') ||
|
|
2381
|
+
const App2 = getCore('Application') || getGlobalScope().Application;
|
|
1817
2382
|
if (!App2 || typeof App2.resetRootView !== 'function') {
|
|
1818
2383
|
console.warn('[hmr-client] Application.resetRootView unavailable');
|
|
1819
2384
|
return false;
|
|
@@ -1832,7 +2397,7 @@ async function performResetRoot(newComponent) {
|
|
|
1832
2397
|
if (VERBOSE)
|
|
1833
2398
|
console.warn('[hmr-client] iOS Application.window is boolean false; attempting to clear cached window');
|
|
1834
2399
|
try {
|
|
1835
|
-
const g =
|
|
2400
|
+
const g = getGlobalScope();
|
|
1836
2401
|
const reg = g.__nsVendorRegistry;
|
|
1837
2402
|
const req = reg?.get ? g.__nsVendorRequire || g.__nsRequire || g.require : g.__nsRequire || g.require;
|
|
1838
2403
|
let helpers = null;
|
|
@@ -1936,6 +2501,20 @@ export function initHmrClient(opts) {
|
|
|
1936
2501
|
}
|
|
1937
2502
|
g.__NS_HMR_CLIENT_ACTIVE__ = true;
|
|
1938
2503
|
ensureCoreAliasesOnGlobalThis();
|
|
2504
|
+
// XML flavor: record string-module modals from the moment the client is up
|
|
2505
|
+
// so an already-open modal can be re-presented when its files change.
|
|
2506
|
+
// Installed at init (not first-update time) because the wrap can only
|
|
2507
|
+
// observe showModal calls made AFTER it lands. Retried briefly because
|
|
2508
|
+
// getCore('View') may not resolve until the vendor realm finishes booting.
|
|
2509
|
+
if (TARGET_FLAVOR === 'typescript') {
|
|
2510
|
+
const tryInstallModalTracking = (attempts) => {
|
|
2511
|
+
ensureModalTracking();
|
|
2512
|
+
if (!modalTrackingInstalled && attempts > 0) {
|
|
2513
|
+
setTimeout(() => tryInstallModalTracking(attempts - 1), 250);
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
tryInstallModalTracking(40);
|
|
2517
|
+
}
|
|
1939
2518
|
// Defer WebSocket connection until boot completes to avoid native V8 crashes
|
|
1940
2519
|
// caused by concurrent WebSocket message handling + HTTP fetch during early startup.
|
|
1941
2520
|
// The WebSocket is only needed for HMR updates, not the initial boot sequence.
|
|
@@ -1957,12 +2536,9 @@ export function initHmrClient(opts) {
|
|
|
1957
2536
|
console.log('[hmr-client] deferring WebSocket connection until boot completes');
|
|
1958
2537
|
setTimeout(waitForBoot, 100);
|
|
1959
2538
|
}
|
|
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
|
-
}
|
|
2539
|
+
// Best-effort: install back wrapper even before first remount; original root may be captured later.
|
|
2540
|
+
// Deferred until the dynamically-imported strategy resolves.
|
|
2541
|
+
void CLIENT_STRATEGY_READY.then(() => CLIENT_STRATEGY?.installBackWrapper?.(performResetRoot, getCore));
|
|
1966
2542
|
}
|
|
1967
2543
|
export default function startViteHMR(opts) {
|
|
1968
2544
|
if (VERBOSE)
|