feat: docker compose maybe
This commit is contained in:
62
node_modules/svelte-hmr/runtime/svelte-native/patch-page-show-modal.js
generated
vendored
Normal file
62
node_modules/svelte-hmr/runtime/svelte-native/patch-page-show-modal.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// This module monkey patches Page#showModal in order to be able to
|
||||
// access from the HMR proxy data passed to `showModal` in svelte-native.
|
||||
//
|
||||
// Data are stored in a opaque prop accessible with `getModalData`.
|
||||
//
|
||||
// It also switches the `closeCallback` option with a custom brewed one
|
||||
// in order to give the proxy control over when its own instance will be
|
||||
// destroyed.
|
||||
//
|
||||
// Obviously this method suffer from extreme coupling with the target code
|
||||
// in svelte-native. So it would be wise to recheck compatibility on SN
|
||||
// version upgrades.
|
||||
//
|
||||
// Relevant code is there (last checked version):
|
||||
//
|
||||
// https://github.com/halfnelson/svelte-native/blob/48fdc97d2eb4d3958cfcb4ff6cf5755a220829eb/src/dom/navigation.ts#L132
|
||||
//
|
||||
|
||||
// FIXME should we override ViewBase#showModal instead?
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { Page } from '@nativescript/core'
|
||||
|
||||
const prop =
|
||||
typeof Symbol !== 'undefined'
|
||||
? Symbol('hmr_svelte_native_modal')
|
||||
: '___HMR_SVELTE_NATIVE_MODAL___'
|
||||
|
||||
const sup = Page.prototype.showModal
|
||||
|
||||
let patched = false
|
||||
|
||||
export const patchShowModal = () => {
|
||||
// guard: already patched
|
||||
if (patched) return
|
||||
patched = true
|
||||
|
||||
Page.prototype.showModal = function(modalView, options) {
|
||||
const modalData = {
|
||||
originalOptions: options,
|
||||
closeCallback: options.closeCallback,
|
||||
}
|
||||
|
||||
modalView[prop] = modalData
|
||||
|
||||
// Proxies to a function that can be swapped on the fly by HMR proxy.
|
||||
//
|
||||
// The default is still to call the original closeCallback from svelte
|
||||
// navtive, which will destroy the modal view & component. This way, if
|
||||
// no HMR happens on the modal content, normal behaviour is preserved
|
||||
// without the proxy having any work to do.
|
||||
//
|
||||
const closeCallback = (...args) => {
|
||||
return modalData.closeCallback(...args)
|
||||
}
|
||||
|
||||
const tamperedOptions = Object.assign({}, options, { closeCallback })
|
||||
|
||||
return sup.call(this, modalView, tamperedOptions)
|
||||
}
|
||||
}
|
||||
|
||||
export const getModalData = modalView => modalView[prop]
|
341
node_modules/svelte-hmr/runtime/svelte-native/proxy-adapter-native.js
generated
vendored
Normal file
341
node_modules/svelte-hmr/runtime/svelte-native/proxy-adapter-native.js
generated
vendored
Normal file
@ -0,0 +1,341 @@
|
||||
/* global document */
|
||||
|
||||
import { adapter as ProxyAdapterDom } from '../proxy-adapter-dom'
|
||||
|
||||
import { patchShowModal, getModalData } from './patch-page-show-modal'
|
||||
|
||||
patchShowModal()
|
||||
|
||||
// Svelte Native support
|
||||
// =====================
|
||||
//
|
||||
// Rerendering Svelte Native page proves challenging...
|
||||
//
|
||||
// In NativeScript, pages are the top level component. They are normally
|
||||
// introduced into NativeScript's runtime by its `navigate` function. This
|
||||
// is how Svelte Natives handles it: it renders the Page component to a
|
||||
// dummy fragment, and "navigate" to the page element thus created.
|
||||
//
|
||||
// As long as modifications only impact child components of the page, then
|
||||
// we can keep the existing page and replace its content for HMR.
|
||||
//
|
||||
// However, if the page component itself is modified (including its system
|
||||
// title bar), things get hairy...
|
||||
//
|
||||
// Apparently, the sole way of introducing a new page in a NS application is
|
||||
// to navigate to it (no way to just replace it in its parent "element", for
|
||||
// example). This is how it is done in NS's own "core" HMR.
|
||||
//
|
||||
// NOTE The last paragraph has not really been confirmed with NS6.
|
||||
//
|
||||
// Unfortunately the API they're using to do that is not public... Its various
|
||||
// parts remain exposed though (but documented as private), so this exploratory
|
||||
// work now relies on it. It might be fragile...
|
||||
//
|
||||
// The problem is that there is no public API that can navigate to a page and
|
||||
// replace (like location.replace) the current history entry. Actually there
|
||||
// is an active issue at NS asking for that. Incidentally, members of
|
||||
// NativeScript-Vue have commented on the issue to weight in for it -- they
|
||||
// probably face some similar challenge.
|
||||
//
|
||||
// https://github.com/NativeScript/NativeScript/issues/6283
|
||||
|
||||
const getNavTransition = ({ transition }) => {
|
||||
if (typeof transition === 'string') {
|
||||
transition = { name: transition }
|
||||
}
|
||||
return transition ? { animated: true, transition } : { animated: false }
|
||||
}
|
||||
|
||||
// copied from TNS FrameBase.replacePage
|
||||
//
|
||||
// it is not public but there is a comment in there indicating it is for
|
||||
// HMR (probably their own core HMR though)
|
||||
//
|
||||
// NOTE this "worked" in TNS 5, but not anymore in TNS 6: updated version bellow
|
||||
//
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const replacePage_tns5 = (frame, newPageElement, hotOptions) => {
|
||||
const currentBackstackEntry = frame._currentEntry
|
||||
frame.navigationType = 2
|
||||
frame.performNavigation({
|
||||
isBackNavigation: false,
|
||||
entry: {
|
||||
resolvedPage: newPageElement.nativeView,
|
||||
//
|
||||
// entry: currentBackstackEntry.entry,
|
||||
entry: Object.assign(
|
||||
currentBackstackEntry.entry,
|
||||
getNavTransition(hotOptions)
|
||||
),
|
||||
navDepth: currentBackstackEntry.navDepth,
|
||||
fragmentTag: currentBackstackEntry.fragmentTag,
|
||||
frameId: currentBackstackEntry.frameId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Updated for TNS v6
|
||||
//
|
||||
// https://github.com/NativeScript/NativeScript/blob/6.1.1/tns-core-modules/ui/frame/frame-common.ts#L656
|
||||
const replacePage = (frame, newPageElement) => {
|
||||
const currentBackstackEntry = frame._currentEntry
|
||||
const newPage = newPageElement.nativeView
|
||||
const newBackstackEntry = {
|
||||
entry: currentBackstackEntry.entry,
|
||||
resolvedPage: newPage,
|
||||
navDepth: currentBackstackEntry.navDepth,
|
||||
fragmentTag: currentBackstackEntry.fragmentTag,
|
||||
frameId: currentBackstackEntry.frameId,
|
||||
}
|
||||
const navigationContext = {
|
||||
entry: newBackstackEntry,
|
||||
isBackNavigation: false,
|
||||
navigationType: 2 /* NavigationType replace */,
|
||||
}
|
||||
frame._navigationQueue.push(navigationContext)
|
||||
frame._processNextNavigationEntry()
|
||||
}
|
||||
|
||||
export const adapter = class ProxyAdapterNative extends ProxyAdapterDom {
|
||||
constructor(instance) {
|
||||
super(instance)
|
||||
|
||||
this.nativePageElement = null
|
||||
this.originalNativeView = null
|
||||
this.navigatedFromHandler = null
|
||||
|
||||
this.relayNativeNavigatedFrom = this.relayNativeNavigatedFrom.bind(this)
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose()
|
||||
this.releaseNativePageElement()
|
||||
}
|
||||
|
||||
releaseNativePageElement() {
|
||||
if (this.nativePageElement) {
|
||||
// native cleaning will happen when navigating back from the page
|
||||
this.nativePageElement = null
|
||||
}
|
||||
}
|
||||
|
||||
// svelte-native uses navigateFrom event + e.isBackNavigation to know
|
||||
// when to $destroy the component -- but we don't want our proxy instance
|
||||
// destroyed when we renavigate to the same page for navigation purposes!
|
||||
interceptPageNavigation(pageElement) {
|
||||
const originalNativeView = pageElement.nativeView
|
||||
const { on } = originalNativeView
|
||||
const ownOn = originalNativeView.hasOwnProperty('on')
|
||||
// tricks svelte-native into giving us its handler
|
||||
originalNativeView.on = function(type, handler) {
|
||||
if (type === 'navigatedFrom') {
|
||||
this.navigatedFromHandler = handler
|
||||
if (ownOn) {
|
||||
originalNativeView.on = on
|
||||
} else {
|
||||
delete originalNativeView.on
|
||||
}
|
||||
} else {
|
||||
//some other handler wireup, we will just pass it on.
|
||||
if (on) {
|
||||
on(type, handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterMount(target, anchor) {
|
||||
// nativePageElement needs to be updated each time (only for page
|
||||
// components, native component that are not pages follow normal flow)
|
||||
//
|
||||
// TODO quid of components that are initially a page, but then have the
|
||||
// <page> tag removed while running? or the opposite?
|
||||
//
|
||||
// insertionPoint needs to be updated _only when the target changes_ --
|
||||
// i.e. when the component is mount, i.e. (in svelte3) when the component
|
||||
// is _created_, and svelte3 doesn't allow it to move afterward -- that
|
||||
// is, insertionPoint only needs to be created once when the component is
|
||||
// first mounted.
|
||||
//
|
||||
// TODO is it really true that components' elements cannot move in the
|
||||
// DOM? what about keyed list?
|
||||
//
|
||||
|
||||
const isNativePage =
|
||||
(target.tagName === 'fragment' || target.tagName === 'frame') &&
|
||||
target.firstChild &&
|
||||
target.firstChild.tagName == 'page'
|
||||
if (isNativePage) {
|
||||
const nativePageElement = target.firstChild
|
||||
this.interceptPageNavigation(nativePageElement)
|
||||
this.nativePageElement = nativePageElement
|
||||
} else {
|
||||
// try to protect against components changing from page to no-page
|
||||
// or vice versa -- see DEBUG 1 above. NOT TESTED so prolly not working
|
||||
this.nativePageElement = null
|
||||
super.afterMount(target, anchor)
|
||||
}
|
||||
}
|
||||
|
||||
rerender() {
|
||||
const { nativePageElement } = this
|
||||
if (nativePageElement) {
|
||||
this.rerenderNative()
|
||||
} else {
|
||||
super.rerender()
|
||||
}
|
||||
}
|
||||
|
||||
rerenderNative() {
|
||||
const { nativePageElement: oldPageElement } = this
|
||||
const nativeView = oldPageElement.nativeView
|
||||
const frame = nativeView.frame
|
||||
if (frame) {
|
||||
return this.rerenderPage(frame, nativeView)
|
||||
}
|
||||
const modalParent = nativeView._modalParent // FIXME private API
|
||||
if (modalParent) {
|
||||
return this.rerenderModal(modalParent, nativeView)
|
||||
}
|
||||
// wtf? hopefully a race condition with a destroyed component, so
|
||||
// we have nothing more to do here
|
||||
//
|
||||
// for once, it happens when hot reloading dev deps, like this file
|
||||
//
|
||||
}
|
||||
|
||||
rerenderPage(frame, previousPageView) {
|
||||
const isCurrentPage = frame.currentPage === previousPageView
|
||||
if (isCurrentPage) {
|
||||
const {
|
||||
instance: { hotOptions },
|
||||
} = this
|
||||
const newPageElement = this.createPage()
|
||||
if (!newPageElement) {
|
||||
throw new Error('Failed to create updated page')
|
||||
}
|
||||
const isFirstPage = !frame.canGoBack()
|
||||
|
||||
if (isFirstPage) {
|
||||
// NOTE not so sure of bellow with the new NS6 method for replace
|
||||
//
|
||||
// The "replacePage" strategy does not work on the first page
|
||||
// of the stack.
|
||||
//
|
||||
// Resulting bug:
|
||||
// - launch
|
||||
// - change first page => HMR
|
||||
// - navigate to other page
|
||||
// - back
|
||||
// => actual: back to OS
|
||||
// => expected: back to page 1
|
||||
//
|
||||
// Fortunately, we can overwrite history in this case.
|
||||
//
|
||||
const nativeView = newPageElement.nativeView
|
||||
frame.navigate(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
create: () => nativeView,
|
||||
clearHistory: true,
|
||||
},
|
||||
getNavTransition(hotOptions)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
replacePage(frame, newPageElement, hotOptions)
|
||||
}
|
||||
} else {
|
||||
const backEntry = frame.backStack.find(
|
||||
({ resolvedPage: page }) => page === previousPageView
|
||||
)
|
||||
if (!backEntry) {
|
||||
// well... looks like we didn't make it to history after all
|
||||
return
|
||||
}
|
||||
// replace existing nativeView
|
||||
const newPageElement = this.createPage()
|
||||
if (newPageElement) {
|
||||
backEntry.resolvedPage = newPageElement.nativeView
|
||||
} else {
|
||||
throw new Error('Failed to create updated page')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// modalParent is the page on which showModal(...) was called
|
||||
// oldPageElement is the modal content, that we're actually trying to reload
|
||||
rerenderModal(modalParent, modalView) {
|
||||
const modalData = getModalData(modalView)
|
||||
|
||||
modalData.closeCallback = () => {
|
||||
const nativePageElement = this.createPage()
|
||||
if (!nativePageElement) {
|
||||
throw new Error('Failed to created updated modal page')
|
||||
}
|
||||
const { nativeView } = nativePageElement
|
||||
const { originalOptions } = modalData
|
||||
// Options will get monkey patched again, the only work left for us
|
||||
// is to try to reduce visual disturbances.
|
||||
//
|
||||
// FIXME Even that proves too much unfortunately... Apparently TNS
|
||||
// does not respect the `animated` option in this context:
|
||||
// https://docs.nativescript.org/api-reference/interfaces/_ui_core_view_base_.showmodaloptions#animated
|
||||
//
|
||||
const options = Object.assign({}, originalOptions, { animated: false })
|
||||
modalParent.showModal(nativeView, options)
|
||||
}
|
||||
|
||||
modalView.closeModal()
|
||||
}
|
||||
|
||||
createPage() {
|
||||
const {
|
||||
instance: { refreshComponent },
|
||||
} = this
|
||||
const { nativePageElement, relayNativeNavigatedFrom } = this
|
||||
const oldNativeView = nativePageElement.nativeView
|
||||
// rerender
|
||||
const target = document.createElement('fragment')
|
||||
// not using conservative for now, since there's nothing in place here to
|
||||
// leverage it (yet?) -- and it might be easier to miss breakages in native
|
||||
// only code paths
|
||||
refreshComponent(target, null)
|
||||
// this.nativePageElement is updated in afterMount, triggered by proxy / hooks
|
||||
const newPageElement = this.nativePageElement
|
||||
// update event proxy
|
||||
oldNativeView.off('navigatedFrom', relayNativeNavigatedFrom)
|
||||
nativePageElement.nativeView.on('navigatedFrom', relayNativeNavigatedFrom)
|
||||
return newPageElement
|
||||
}
|
||||
|
||||
relayNativeNavigatedFrom({ isBackNavigation }) {
|
||||
const { originalNativeView, navigatedFromHandler } = this
|
||||
if (!isBackNavigation) {
|
||||
return
|
||||
}
|
||||
if (originalNativeView) {
|
||||
const { off } = originalNativeView
|
||||
const ownOff = originalNativeView.hasOwnProperty('off')
|
||||
originalNativeView.off = function() {
|
||||
this.navigatedFromHandler = null
|
||||
if (ownOff) {
|
||||
originalNativeView.off = off
|
||||
} else {
|
||||
delete originalNativeView.off
|
||||
}
|
||||
}
|
||||
}
|
||||
if (navigatedFromHandler) {
|
||||
return navigatedFromHandler.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
renderError(err /* , target, anchor */) {
|
||||
// TODO fallback on TNS error handler for now... at least our error
|
||||
// is more informative
|
||||
throw err
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user