refactor(node-watcher): adapt to new DI architecture
This commit moves the node watcher code to the presentation layer and
adapts it to be ready to use DI. Thus, the node watcher is separated
into a port and adapter.
Bug: twpowertools:226
Change-Id: Id36d5407ff478006eb8c057db1dcad05fd30b7d6
diff --git a/src/common/architecture/scripts/nodeWatcher/LegacyNodeWatcherScript.ts b/src/common/architecture/scripts/nodeWatcher/LegacyNodeWatcherScript.ts
index b4c07a5..211210d 100644
--- a/src/common/architecture/scripts/nodeWatcher/LegacyNodeWatcherScript.ts
+++ b/src/common/architecture/scripts/nodeWatcher/LegacyNodeWatcherScript.ts
@@ -1,4 +1,4 @@
-import NodeWatcherSingleton from '../../../nodeWatcher/NodeWatcher';
+import NodeWatcherSingleton, { NodeWatcherAdapter } from '../../../../infrastructure/presentation/nodeWatcher/NodeWatcher.adapter';
import { ConcreteNodeWatcherScriptHandler } from './handlers/NodeWatcherScriptHandler';
import Script from '../Script';
@@ -11,22 +11,28 @@
ConcreteNodeWatcherScriptHandler<Options>
>;
+ private nodeWatcher: NodeWatcherAdapter;
+
+ constructor() {
+ super();
+
+ // TODO(https://iavm.xyz/b/226): Retrieve this via constructor injection.
+ this.nodeWatcher = NodeWatcherSingleton.getInstance();
+ }
+
/**
* Resolves to the options when the script is executed.
*
- * This is so we can defer retrieving dependencies until the script is
- * executed, to prevent loading unnecessary things if they aren't needed
- * after all.
*/
protected abstract optionsFactory(): Options;
execute() {
- const nodeWatcher = NodeWatcherSingleton.getInstance();
const options = this.optionsFactory();
+ this.nodeWatcher.start();
for (const [key, handlerClass] of this.handlers) {
const handler = new handlerClass(options);
- nodeWatcher.setHandler(key, handler);
+ this.nodeWatcher.setHandler(key, handler);
}
}
}
diff --git a/src/common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler.ts b/src/common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler.ts
index b5c3698..8756b72 100644
--- a/src/common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler.ts
+++ b/src/common/architecture/scripts/nodeWatcher/handlers/CssSelectorNodeWatcherScriptHandler.ts
@@ -1,5 +1,5 @@
import { NodeWatcherScriptHandler } from './NodeWatcherScriptHandler';
-import { NodeMutation, NodeMutationType } from '../../../../nodeWatcher/NodeWatcherHandler';
+import { NodeMutation, NodeMutationType } from '../../../../../presentation/nodeWatcher/NodeWatcherHandler';
/**
* @deprecated
diff --git a/src/common/architecture/scripts/nodeWatcher/handlers/NodeWatcherScriptHandler.ts b/src/common/architecture/scripts/nodeWatcher/handlers/NodeWatcherScriptHandler.ts
index 5b0fd42..712f533 100644
--- a/src/common/architecture/scripts/nodeWatcher/handlers/NodeWatcherScriptHandler.ts
+++ b/src/common/architecture/scripts/nodeWatcher/handlers/NodeWatcherScriptHandler.ts
@@ -1,4 +1,4 @@
-import { NodeMutation, NodeWatcherHandler } from "../../../../nodeWatcher/NodeWatcherHandler";
+import { NodeMutation, NodeWatcherHandler } from "../../../../../presentation/nodeWatcher/NodeWatcherHandler";
export abstract class NodeWatcherScriptHandler<Options>
implements NodeWatcherHandler
diff --git a/src/common/nodeWatcher/NodeWatcher.ts b/src/common/nodeWatcher/NodeWatcher.ts
deleted file mode 100644
index 73b9560..0000000
--- a/src/common/nodeWatcher/NodeWatcher.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import {
- NodeWatcherHandler,
- NodeMutation,
- NodeMutationType,
-} from './NodeWatcherHandler';
-
-class NodeWatcher {
- private handlers: Map<string, NodeWatcherHandler> = new Map();
- private mutationObserver: MutationObserver;
-
- constructor() {
- this.mutationObserver = new MutationObserver(
- this.mutationCallback.bind(this),
- );
- this.start();
- }
-
- start(): void {
- this.mutationObserver.observe(document.body, {
- childList: true,
- subtree: true,
- });
- }
-
- pause(): void {
- this.mutationObserver.disconnect();
- }
-
- setHandler(key: string, handler: NodeWatcherHandler): void {
- this.handlers.set(key, handler);
- this.performInitialDiscovery(handler);
- }
-
- removeHandler(key: string): boolean {
- return this.handlers.delete(key);
- }
-
- private mutationCallback(mutationRecords: MutationRecord[]): void {
- for (const mutationRecord of mutationRecords) {
- if (mutationRecord.type !== 'childList') continue;
- this.handleAddedNodes(mutationRecord);
- this.handleRemovedNodes(mutationRecord);
- }
- }
-
- private handleAddedNodes(mutationRecord: MutationRecord): void {
- for (const node of mutationRecord.addedNodes) {
- this.handleMutatedNode({
- node,
- mutationRecord,
- type: NodeMutationType.NewNode,
- });
- }
- }
-
- private handleRemovedNodes(mutationRecord: MutationRecord): void {
- for (const node of mutationRecord.removedNodes) {
- this.handleMutatedNode({
- node,
- mutationRecord,
- type: NodeMutationType.RemovedNode,
- });
- }
- }
-
- private performInitialDiscovery(handler: NodeWatcherHandler): void {
- if (handler.initialDiscoverySelector === undefined) return;
- const candidateNodes = document.querySelectorAll(
- handler.initialDiscoverySelector,
- );
- for (const candidateNode of candidateNodes) {
- this.handleMutatedNodeWithHandler(
- {
- node: candidateNode,
- type: NodeMutationType.InitialDiscovery,
- mutationRecord: null,
- },
- handler,
- );
- }
- }
-
- private handleMutatedNode(nodeMutation: NodeMutation): void {
- for (const [, handler] of this.handlers) {
- this.handleMutatedNodeWithHandler(nodeMutation, handler);
- }
- }
-
- private handleMutatedNodeWithHandler(
- nodeMutation: NodeMutation,
- handler: NodeWatcherHandler,
- ): void {
- if (handler.nodeFilter(nodeMutation)) {
- handler.onMutatedNode(nodeMutation);
- }
- }
-}
-
-export default class NodeWatcherSingleton {
- private static instance: NodeWatcher;
-
- /**
- * @see {@link NodeWatcherSingleton.getInstance}
- */
- private constructor() {}
-
- public static getInstance(): NodeWatcher {
- if (!NodeWatcherSingleton.instance) {
- NodeWatcherSingleton.instance = new NodeWatcher();
- }
- return NodeWatcherSingleton.instance;
- }
-}
diff --git a/src/common/nodeWatcher/NodeWatcherHandler.ts b/src/common/nodeWatcher/NodeWatcherHandler.ts
deleted file mode 100644
index b6c3a25..0000000
--- a/src/common/nodeWatcher/NodeWatcherHandler.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-export enum NodeMutationType {
- /**
- * The node was found during initial discovery.
- */
- InitialDiscovery,
- /**
- * The node has been added.
- */
- NewNode,
- /**
- * The node was removed
- */
- RemovedNode,
-}
-
-export interface NodeMutation {
- /**
- * Node being mutated.
- */
- node: Node;
- /**
- * Which mutation has occurred to the node.
- */
- type: NodeMutationType;
- /**
- * MutationRecord from which this node mutation has been extracted. It is null
- * if the type is {@link NodeMutationType.InitialDiscovery}.
- */
- mutationRecord: MutationRecord | null;
-}
-
-export interface NodeWatcherHandler {
- /**
- * Only node mutations which pass this filter (it returns true) will be passed
- * to {@link onMutatedNode}.
- */
- nodeFilter: (nodeMutation: NodeMutation) => boolean;
-
- /**
- * Optional CSS selector used to discover nodes existing prior to the handler
- * being established. These matching nodes will be evaluated by
- * {@link onMutatedNode} if they pass {@link nodeFilter}.
- *
- * This is useful when watching an node but it has already been created.
- */
- initialDiscoverySelector?: string;
-
- /**
- * Function which will be called with each of the filtered node mutations.
- */
- onMutatedNode: (nodeMutation: NodeMutation) => void;
-}