209 lines
6.2 KiB
JavaScript
209 lines
6.2 KiB
JavaScript
// Options for Preact.
|
|
import './runtime/catchError';
|
|
import './runtime/debounceRendering';
|
|
import './runtime/vnode';
|
|
import './runtime/unmount';
|
|
|
|
import { Component } from 'preact';
|
|
|
|
import {
|
|
VNODE_COMPONENT,
|
|
NAMESPACE,
|
|
HOOKS_LIST,
|
|
EFFECTS_LIST,
|
|
COMPONENT_HOOKS,
|
|
HOOK_ARGS,
|
|
HOOK_VALUE,
|
|
HOOK_CLEANUP,
|
|
} from './constants';
|
|
import { computeKey } from './computeKey';
|
|
import { vnodesForComponent, mappedVNodes } from './runtime/vnodesForComponent';
|
|
import { signaturesForType } from './runtime/signaturesForType';
|
|
|
|
let typesById = new Map();
|
|
let pendingUpdates = [];
|
|
|
|
function sign(type, key, forceReset, getCustomHooks, status) {
|
|
if (type) {
|
|
let signature = signaturesForType.get(type);
|
|
if (status === 'begin') {
|
|
signaturesForType.set(type, {
|
|
type,
|
|
key,
|
|
forceReset,
|
|
getCustomHooks: getCustomHooks || (() => []),
|
|
});
|
|
|
|
return 'needsHooks';
|
|
} else if (status === 'needsHooks') {
|
|
signature.fullKey = computeKey(signature);
|
|
}
|
|
}
|
|
}
|
|
|
|
function replaceComponent(OldType, NewType, resetHookState) {
|
|
const vnodes = vnodesForComponent.get(OldType);
|
|
if (!vnodes) return;
|
|
|
|
// migrate the list to our new constructor reference
|
|
vnodesForComponent.delete(OldType);
|
|
vnodesForComponent.set(NewType, vnodes);
|
|
|
|
mappedVNodes.set(OldType, NewType);
|
|
|
|
pendingUpdates = pendingUpdates.filter(p => p[0] !== OldType);
|
|
|
|
vnodes.forEach(vnode => {
|
|
if (!vnode.__c || !vnode.__c.__P) return;
|
|
// update the type in-place to reference the new component
|
|
vnode.type = NewType;
|
|
|
|
if (vnode[VNODE_COMPONENT]) {
|
|
vnode[VNODE_COMPONENT].constructor = vnode.type;
|
|
|
|
try {
|
|
if (vnode[VNODE_COMPONENT] instanceof OldType) {
|
|
const oldInst = vnode[VNODE_COMPONENT];
|
|
|
|
const newInst = new NewType(
|
|
vnode[VNODE_COMPONENT].props,
|
|
vnode[VNODE_COMPONENT].context
|
|
);
|
|
|
|
vnode[VNODE_COMPONENT] = newInst;
|
|
// copy old properties onto the new instance.
|
|
// - Objects (including refs) in the new instance are updated with their old values
|
|
// - Missing or null properties are restored to their old values
|
|
// - Updated Functions are not reverted
|
|
// - Scalars are copied
|
|
for (let i in oldInst) {
|
|
const type = typeof oldInst[i];
|
|
if (!(i in newInst)) {
|
|
newInst[i] = oldInst[i];
|
|
} else if (type !== 'function' && typeof newInst[i] === type) {
|
|
if (
|
|
type === 'object' &&
|
|
newInst[i] != null &&
|
|
newInst[i].constructor === oldInst[i].constructor
|
|
) {
|
|
Object.assign(newInst[i], oldInst[i]);
|
|
} else {
|
|
newInst[i] = oldInst[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
/* Functional component */
|
|
vnode[VNODE_COMPONENT].constructor = NewType;
|
|
}
|
|
|
|
if (resetHookState) {
|
|
if (
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS] &&
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST] &&
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST].length
|
|
) {
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST].forEach(
|
|
possibleEffect => {
|
|
if (
|
|
possibleEffect[HOOK_CLEANUP] &&
|
|
typeof possibleEffect[HOOK_CLEANUP] === 'function'
|
|
) {
|
|
possibleEffect[HOOK_CLEANUP]();
|
|
possibleEffect[HOOK_CLEANUP] = undefined;
|
|
} else if (
|
|
possibleEffect[HOOK_ARGS] &&
|
|
possibleEffect[HOOK_VALUE] &&
|
|
Object.keys(possibleEffect).length === 3
|
|
) {
|
|
const cleanupKey = Object.keys(possibleEffect).find(
|
|
key => key !== HOOK_ARGS && key !== HOOK_VALUE
|
|
);
|
|
if (
|
|
cleanupKey &&
|
|
typeof possibleEffect[cleanupKey] == 'function'
|
|
) {
|
|
possibleEffect[cleanupKey]();
|
|
possibleEffect[cleanupKey] = undefined;
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS] = {
|
|
[HOOKS_LIST]: [],
|
|
[EFFECTS_LIST]: [],
|
|
};
|
|
} else if (
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS] &&
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST] &&
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST].length
|
|
) {
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST].forEach(
|
|
possibleEffect => {
|
|
if (
|
|
possibleEffect[HOOK_CLEANUP] &&
|
|
typeof possibleEffect[HOOK_CLEANUP] === 'function'
|
|
) {
|
|
possibleEffect[HOOK_CLEANUP]();
|
|
possibleEffect[HOOK_CLEANUP] = undefined;
|
|
} else if (
|
|
possibleEffect[HOOK_ARGS] &&
|
|
possibleEffect[HOOK_VALUE] &&
|
|
Object.keys(possibleEffect).length === 3
|
|
) {
|
|
const cleanupKey = Object.keys(possibleEffect).find(
|
|
key => key !== HOOK_ARGS && key !== HOOK_VALUE
|
|
);
|
|
if (cleanupKey && typeof possibleEffect[cleanupKey] == 'function')
|
|
possibleEffect[cleanupKey]();
|
|
possibleEffect[cleanupKey] = undefined;
|
|
}
|
|
}
|
|
);
|
|
|
|
vnode[VNODE_COMPONENT][COMPONENT_HOOKS][HOOKS_LIST].forEach(hook => {
|
|
if (hook.__H && Array.isArray(hook.__H)) {
|
|
hook.__H = undefined;
|
|
}
|
|
});
|
|
}
|
|
|
|
Component.prototype.forceUpdate.call(vnode[VNODE_COMPONENT]);
|
|
}
|
|
});
|
|
}
|
|
|
|
self[NAMESPACE] = {
|
|
getSignature: type => signaturesForType.get(type),
|
|
register: (type, id) => {
|
|
if (typeof type !== 'function') return;
|
|
|
|
if (typesById.has(id)) {
|
|
const existing = typesById.get(id);
|
|
if (existing !== type) {
|
|
pendingUpdates.push([existing, type]);
|
|
typesById.set(id, type);
|
|
}
|
|
} else {
|
|
typesById.set(id, type);
|
|
}
|
|
|
|
if (!signaturesForType.has(type)) {
|
|
signaturesForType.set(type, {
|
|
getCustomHooks: () => [],
|
|
type,
|
|
});
|
|
}
|
|
},
|
|
getPendingUpdates: () => pendingUpdates,
|
|
flush: () => {
|
|
pendingUpdates = [];
|
|
},
|
|
replaceComponent,
|
|
sign,
|
|
computeKey,
|
|
};
|