@adpt/cloud
Version:
AdaptJS cloud component library
124 lines • 4.81 kB
JavaScript
;
/*
* Copyright 2019 Unbounded Systems, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@adpt/core");
const utils_1 = require("@adpt/utils");
const Action_1 = require("./Action");
// FIXME(mark): The use of componentName here may not be quite sufficient.
// Since it's this ID that matches up Elements from the old and new DOM,
// this indirectly decides whether we either perform a
// delete-old-element plus create-new-element or just an update-new-element.
// So if the Element at a certain ID is replaced with a component of the
// same name (and still same ID), BUT has a different implementation or
// semantics, we won't detect that here.
exports.idFunc = (el) => `${el.id}:${el.componentName}`;
class ActionPlugin {
constructor() {
this.elements = new Map();
}
async start(options) {
if (options.logger == null)
throw new Error(`Plugin start called without logger`);
this.logger = options.logger;
if (options.dataDir == null)
throw new Error(`Plugin start called without dataDir`);
this.dataDir = options.dataDir;
}
async observe(oldDom, newDom) {
const obs = {};
const callShould = async (list, op) => {
for (const el of list) {
const context = this.context(el);
const inst = Action_1.getActionInstance(el);
if (!inst)
continue;
const id = exports.idFunc(el);
this.elements.set(id, el);
const ret = Action_1.toDetail(await inst.shouldAct(op, context));
obs[id] = {
type: ret.act ? op : core_1.ChangeType.none,
detail: ret.detail,
};
}
};
const diff = core_1.domDiff(oldDom, newDom, exports.idFunc);
await callShould(diff.deleted, core_1.ChangeType.delete);
await callShould(diff.added, core_1.ChangeType.create);
await callShould(diff.commonNew, core_1.ChangeType.modify);
return obs;
}
analyze(_oldDom, _newDom, observations) {
// Aggregate all "none" items into one PluginAction
const noAction = [];
const actions = Object.keys(observations).map((id) => {
const obs = observations[id];
const el = this.elements.get(id);
if (!el)
throw new Error(`Internal error: unable to look up element for ID ${id}`);
const context = this.context(el);
if (obs.type === core_1.ChangeType.none) {
noAction.push(el);
return null;
}
const inst = Action_1.getActionInstance(el);
if (!inst)
throw new Error(`Unexpected error getting Action instance`);
return {
type: obs.type,
detail: obs.detail,
act: async () => inst.action(obs.type, context),
changes: [{
type: obs.type,
detail: obs.detail,
element: el,
}]
};
}).filter(utils_1.notNull);
if (noAction.length > 0) {
actions.push({
type: core_1.ChangeType.none,
detail: "No action required",
act: async () => { },
changes: noAction.map((el) => ({
type: core_1.ChangeType.none,
detail: "No action required",
element: el,
}))
});
}
return actions;
}
async finish() { }
context(el) {
const logger = this.logger;
const dataDir = this.dataDir;
if (!logger || !dataDir)
throw new Error(`Plugin not initialized correctly`);
return { buildData: el.buildData, dataDir, logger };
}
}
exports.ActionPlugin = ActionPlugin;
function createActionPlugin() {
return new ActionPlugin();
}
exports.createActionPlugin = createActionPlugin;
core_1.registerPlugin({
name: "Action",
module,
create: createActionPlugin,
});
//# sourceMappingURL=action_plugin.js.map