You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ge...@apache.org on 2020/09/03 15:25:38 UTC
[incubator-annotator] 03/03: WIP Some steps to a middleware/plugin
system
This is an automated email from the ASF dual-hosted git repository.
gerben pushed a commit to branch simpler-matcher-creation
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 8db8de3611afc22b6eb8ec343ac9878fdd6054c8
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Sep 3 17:24:04 2020 +0200
WIP Some steps to a middleware/plugin system
---
packages/selector/src/index.ts | 71 +++++++++++++++++++++++++++++++-----------
web/demo/index.js | 23 ++++++++++----
2 files changed, 69 insertions(+), 25 deletions(-)
diff --git a/packages/selector/src/index.ts b/packages/selector/src/index.ts
index b95a858..5840439 100644
--- a/packages/selector/src/index.ts
+++ b/packages/selector/src/index.ts
@@ -18,36 +18,69 @@
* under the License.
*/
-import type { Matcher, Selector } from './types';
+import type { Matcher, Selector, SelectorType } from './types';
export type { Matcher, Selector } from './types';
export type { CssSelector, RangeSelector, TextQuoteSelector } from './types';
-export function createTypedMatcherCreator<TSelectorType extends string, TScope, TMatch extends TScope>(
- typeToMatcher:
- | Record<TSelectorType, ((selector: Selector) => Matcher<TScope, TMatch>)>
- | ((type: TSelectorType) => (selector: Selector) => Matcher<TScope, TMatch>),
-): (selector: Selector & { type: TSelectorType }) => Matcher<TScope, TMatch> {
+interface TypeToMatcherCreatorMap<TScope, TMatch> {
+ // [K: SelectorType]: MatcherCreator<TScope, TMatch>; // Gives errors further down. TypeScript’s fault?
+ [K: string]: MatcherCreator<TScope, TMatch> | undefined;
+}
- function createMatcher(selector: Selector & { type: TSelectorType }): Matcher<TScope, TMatch> {
- const type = selector.type;
+type MatcherCreator<TScope, TMatch> = (selector: Selector) => Matcher<TScope, TMatch>;
+type Plugin<TScope, TMatch> =
+ (
+ next: MatcherCreator<TScope, TMatch>,
+ recurse: MatcherCreator<TScope, TMatch>,
+ ) => typeof next;
- if (type === undefined) {
- throw new TypeError('Selector does not specify its type');
- }
+const identity: Plugin<any, any> = (next, recurse) => next;
- const innerCreateMatcher = (typeof typeToMatcher === 'function')
- ? typeToMatcher(type)
- : typeToMatcher[type];
+// simply equals makeRefinable!
+export const supportRefinement: Plugin<any, any> =
+ <TScope, TMatch extends TScope>(
+ next: MatcherCreator<TScope, TMatch>,
+ recurse: MatcherCreator<TScope, TMatch>,
+ ) => {
+ return makeRefinable(next);
+ };
- if (innerCreateMatcher === undefined) {
- throw new TypeError(`Unsupported selector type: ${type}`);
- }
+export function composeMatcherCreator<TScope, TMatch extends TScope>(
+ ...plugins: Array<Plugin<TScope, TMatch>>
+): MatcherCreator<TScope, TMatch> {
+ function innerMatcherCreator(selector: Selector): Matcher<TScope, TMatch> {
+ throw new TypeError(`Unhandled selector. Selector type: ${selector.type}`);
+ }
- return innerCreateMatcher(selector);
+ function outerMatcherCreator(selector: Selector): Matcher<TScope, TMatch> {
+ return composedMatcherCreator(selector);
}
- return makeRefinable(createMatcher);
+ const composedMatcherCreator = plugins.reduce(
+ (matcherCreator: MatcherCreator<TScope, TMatch>, plugin: Plugin<TScope, TMatch>) => plugin(matcherCreator, outerMatcherCreator),
+ innerMatcherCreator,
+ );
+
+ return outerMatcherCreator;
+}
+
+// Invokes the matcher implementation corresponding to the selector’s type.
+export function mapSelectorTypes<TScope, TMatch extends TScope>(
+ typeToMatcherCreator: TypeToMatcherCreatorMap<TScope, TMatch>,
+): Plugin<TScope, TMatch> {
+ return function(next, recurse): MatcherCreator<TScope, TMatch> {
+ return function(selector: Selector): Matcher<TScope, TMatch> {
+ const type = selector.type;
+ if (type !== undefined) {
+ const matcherCreator = typeToMatcherCreator[type];
+ if (matcherCreator !== undefined)
+ return matcherCreator(selector);
+ }
+ // Not a know selector type; continue down the plugin chain.
+ return next(selector);
+ }
+ }
}
export function makeRefinable<
diff --git a/web/demo/index.js b/web/demo/index.js
index 16c9d03..44597c3 100644
--- a/web/demo/index.js
+++ b/web/demo/index.js
@@ -19,14 +19,22 @@
*/
/* global info, module, source, target */
+// declare const module; // TODO type?
+// declare const info: HTMLElement;
+// declare const source: HTMLElement;
+// declare const target: HTMLElement;
import {
- makeCreateRangeSelectorMatcher,
createTextQuoteSelectorMatcher,
describeTextQuote,
highlightRange,
} from '@annotator/dom';
-import { createTypedMatcherCreator } from '@annotator/selector';
+import {
+ composeMatcherCreator,
+ mapSelectorTypes,
+ // supportRangeSelector,
+ supportRefinement,
+} from '@annotator/selector';
const EXAMPLE_SELECTORS = [
{
@@ -91,10 +99,13 @@ function cleanup() {
target.normalize();
}
-const createMatcher = createTypedMatcherCreator({
- TextQuoteSelector: createTextQuoteSelectorMatcher,
- RangeSelector: makeCreateRangeSelectorMatcher(createMatcher), // FIXME This goes wrong. Tough!
-});
+const createMatcher = composeMatcherCreator(
+ supportRefinement,
+ mapSelectorTypes({
+ TextQuoteSelector: createTextQuoteSelectorMatcher,
+ }),
+ // supportRangeSelector,
+);
async function anchor(selector) {
const matchAll = createMatcher(selector);