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 <[email protected]> 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);
