UNPKG

aurelia-hot-module-reload

Version:

Tools designed to enable HMR for Aurelia's loaders.

124 lines (102 loc) 4.62 kB
import { TargetInstruction, Controller, View, BehaviorInstruction } from 'aurelia-templating'; import { AUController, ViewFactoryWithTemplate, ViewCorrect, ViewSlotCorrect } from './_typings'; import { Container } from 'aurelia-dependency-injection'; export function recreateView(viewFactory: ViewFactoryWithTemplate, oldViewContainer: Container) { let parentContainer = oldViewContainer.parent || oldViewContainer; const targetInstruction = oldViewContainer.get(TargetInstruction) as TargetInstruction; // const targetInstruction = container.get(TargetInstruction) as TargetInstruction; let factoryCreateInstruction = targetInstruction.elementInstruction || ({partReplacements: null} as BehaviorInstruction); // let factoryCreateInstruction = ({partReplacements: null} as BehaviorInstruction); // console.log(`new element instruction`, targetInstruction, factoryCreateInstruction); const newContainer = parentContainer.createChild(); newContainer._resolvers = oldViewContainer._resolvers; // const newContainer = oldViewContainer; const newView = viewFactory.create(newContainer, factoryCreateInstruction) as ViewCorrect; newView._isUserControlled = true; return newView as ViewCorrect; } export function cleanupView(view: ViewCorrect) { const firstChild = view.firstChild; const lastChild = view.lastChild; const nextSibling = lastChild.nextSibling; const parent = firstChild.parentElement; const { bindingContext, overrideContext, container } = view; view.removeNodes(); let wasAttached = view.isAttached; if (wasAttached) { view.detached(); } let wasBound = view.isBound; if (wasBound) { view.unbind(); } view._invalidView = true; return {nextSibling, parent, wasBound, wasAttached, bindingContext, overrideContext, container} } export function rerenderController(e: AUController, type: 'scope' | 'view', newViewFactory: ViewFactoryWithTemplate) { let oldView = e[type] as ViewCorrect; if (!oldView) { // view was removed from the controller in a previous run, ignore return } if (oldView._invalidView) { // previously re-rendered, ensure controller is set and skip if (oldView._replacementView) { e[type] = oldView._replacementView; } return; } const { nextSibling, parent, wasBound, wasAttached, bindingContext, overrideContext, container: oldViewContainer } = cleanupView(oldView) // create & add view: const newView = oldView._replacementView = e[type] = recreateView(newViewFactory || oldView.viewFactory, oldViewContainer); if (!newView.isBound && wasBound) { newView.bind(bindingContext, overrideContext); } if (nextSibling) { newView.insertNodesBefore(nextSibling); } else { newView.appendNodesTo(parent as HTMLElement); } if (!newView.isAttached && wasAttached) { newView.attached(); } } export function rerenderMatchingSlotChildren(slot: ViewSlotCorrect, newViewFactory?: ViewFactoryWithTemplate, originalFactoryTemplate?: any, onlyViews?: Array<ViewCorrect>) { const previousChildren = slot.children.slice() const viewsToReplace = previousChildren.filter(view => (onlyViews && onlyViews.indexOf(view) >= 0) || (view.viewFactory && view.viewFactory.template === originalFactoryTemplate)) const bindingContexts = new Map<View, any>() const overrideContexts = new Map<View, any>() const controllers = new Map<View, Controller>() viewsToReplace.forEach(oldView => { // store contexts because they'll be removed when unbound: bindingContexts.set(oldView, oldView.bindingContext); overrideContexts.set(oldView, oldView.overrideContext); controllers.set(oldView, oldView.controller); if (oldView.isBound) { oldView.unbind(); } oldView._invalidView = true }) slot.removeMany(viewsToReplace, false, true); // recreate removed Views in the same place: previousChildren.forEach((oldView, index) => { if (!oldView._invalidView) { // don't do anything to non-matching Views return } const bindingContext = bindingContexts.get(oldView); const overrideContext = overrideContexts.get(oldView); const controller = controllers.get(oldView); const view = recreateView(newViewFactory || oldView.viewFactory, oldView.container); // setup _replacementView in case the same view is looped over again oldView._replacementView = view; if (controller) { controller.view = view } if (!view.isBound) { view.bind(bindingContext, overrideContext); } // indicies should match up and grow as we iterate up slot.insert(index, view); }); }