Skip to content

Commit

Permalink
feat: convert mention extension to new api
Browse files Browse the repository at this point in the history
  • Loading branch information
ifiokjr committed Apr 13, 2020
1 parent 495966a commit fde15ac
Show file tree
Hide file tree
Showing 27 changed files with 354 additions and 296 deletions.
4 changes: 2 additions & 2 deletions @remirror/core-constants/src/error-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export enum ErrorConstant {
UNKNOWN = 'RMR0001',

/**
* You can only pass `extraAttributes` to a node extension or a mark extension.
* The arguments passed to the command method were invalid.
*/
EXTRA_ATTRIBUTES = 'RMR0002',
INVALID_COMMAND_ARGUMENTS = 'RMR0002',

/**
* This is a custom error possibly thrown by an external library.
Expand Down
40 changes: 23 additions & 17 deletions @remirror/core-helpers/src/core-errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,49 @@ let errorMessageMap: Partial<Record<ErrorConstant, string>> = {};
// This will be removed in a production environment.
if (process.env.NODE !== 'production') {
errorMessageMap = {
RMR0000: 'Production error. No details available.',
RMR0001: "An error occurred but we're not quite sure why. 🧐",
RMR0002: 'You can only pass `extraAttributes` to a node extension or a mark extension.',
RMR0003: 'This is a custom error, possibly thrown by an external library.',
RMR0004: 'An error occurred in a function called from the `@remirror/core-helpers` library.',
RMR0005: 'Mutation of immutable value detected.',
RMR0006: 'This is an error which should not occur and is internal to the remirror codebase.',
RMR0007: 'Your editor is missing a required extension.',
RMR0008:
[ErrorConstant.PROD]: 'Production error. No details available.',
[ErrorConstant.UNKNOWN]: "An error occurred but we're not quite sure why. 🧐",
[ErrorConstant.INVALID_COMMAND_ARGUMENTS]:
'The arguments passed to the command method were invalid.',
[ErrorConstant.CUSTOM]: 'This is a custom error, possibly thrown by an external library.',
[ErrorConstant.CORE_HELPERS]:
'An error occurred in a function called from the `@remirror/core-helpers` library.',
[ErrorConstant.MUTATION]: 'Mutation of immutable value detected.',
[ErrorConstant.INTERNAL]:
'This is an error which should not occur and is internal to the remirror codebase.',
[ErrorConstant.MISSING_REQUIRED_EXTENSION]: 'Your editor is missing a required extension.',
[ErrorConstant.MANAGER_PHASE_ERROR]:
'Called a method event at the wrong time. Please make sure getter functions are only called with within the scope of the returned functions. They should not be called in the outer scope of your method.',

RMR0010:
[ErrorConstant.INVALID_PRESET_EXTENSION]:
'You requested an invalid extension from the preset. Please check the `createExtensions` return method is returning an extension with the requested constructor.',
RMR0011:
[ErrorConstant.INVALID_MANAGER_ARGUMENTS]:
'Invalid value(s) passed into `Manager` constructor. Only `Presets` and `Extensions` are supported.',
RMR0012:
[ErrorConstant.COMMANDS_CALLED_IN_OUTER_SCOPE]:
'The commands method which is passed into the `createCommands` function should only be called within the created command function otherwise it will not have access to the other commands.',
RMR0013: 'You requested an invalid extension from the manager.',
[ErrorConstant.INVALID_MANAGER_EXTENSION]:
'You requested an invalid extension from the manager.',
[ErrorConstant.INVALID_MANAGER_PRESET]: '',
};
}

/**
* Checks whether the passed code is an `ErrorConstant`.
*/
const isErrorConstant = (code: unknown): code is ErrorConstant =>
isString(code) && includes(values(ErrorConstant), code);
function isErrorConstant(code: unknown): code is ErrorConstant {
return isString(code) && includes(values(ErrorConstant), code);
}

/**
* Create an error message from the provided code.
*/
const createErrorMessage = (code: ErrorConstant, extraMessage?: string) => {
function createErrorMessage(code: ErrorConstant, extraMessage?: string) {
const message = errorMessageMap[code];
const prefix = message ? `${message}\n` : '';
const customMessage = extraMessage ? `${extraMessage}\n` : '';

return `${prefix}${customMessage}For more information visit ${ERROR_INFORMATION_URL}#${code}`;
};
}

/**
* This marks the error as a remirror specific error, with enhanced stack
Expand Down
84 changes: 51 additions & 33 deletions @remirror/core-helpers/src/core-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { nanoid } from 'nanoid';
import objectOmit from 'object.omit';

import { REMIRROR_IDENTIFIER_KEY, RemirrorIdentifier } from '@remirror/core-constants';
import { Predicate, RemirrorIdentifierShape } from '@remirror/core-types';
import { Nullable, Predicate, RemirrorIdentifierShape } from '@remirror/core-types';

type AnyConstructor<GType = unknown> = new (...args: any[]) => GType;
type AnyFunction<GType = any> = (...args: any[]) => GType;
Expand Down Expand Up @@ -439,9 +439,9 @@ export const isIdentifierOfType = (value: RemirrorIdentifierShape, type: Remirro
* @param str - the string to capitalize.
* @public
*/
export const capitalize = (string: string) => {
export function capitalize(string: string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
}

/**
* Removes leading and trailing whitespace from a string.
Expand All @@ -450,9 +450,9 @@ export const capitalize = (string: string) => {
*
* @public
*/
export const trim = (string: string) => {
export function trim(string: string) {
return string.replace(/^ +| +$/g, '');
};
}

/**
* Trim and conditionally capitalize string values.
Expand All @@ -461,10 +461,10 @@ export const trim = (string: string) => {
*
* @public
*/
export const format = (string: string) => {
export function format(string: string) {
string = trim(string);
return /^(?:webOS|i(?:OS|P))/.test(string) ? string : capitalize(string);
};
}

/**
* Calls a function if defined and provides compile time type checking for the
Expand All @@ -473,14 +473,14 @@ export const format = (string: string) => {
* @param fn - the function to call if it exists
* @param args - the rest of the parameters with types
*/
export const callIfDefined = <GFunc extends AnyFunction>(
fn: GFunc | unknown,
...arguments_: Parameters<GFunc>
) => {
export function callIfDefined<Method extends AnyFunction>(
fn: Nullable<Method>,
...args: Parameters<Method>
) {
if (isFunction(fn)) {
fn(...arguments_);
fn(...args);
}
};
}

/**
* Finds all the regex matches for a string
Expand All @@ -490,7 +490,7 @@ export const callIfDefined = <GFunc extends AnyFunction>(
*
* @public
*/
export const findMatches = (text: string, regexp: RegExp) => {
export function findMatches(text: string, regexp: RegExp) {
const results: RegExpExecArray[] = [];
const flags = regexp.flags;
let match: RegExpExecArray | null;
Expand All @@ -508,7 +508,7 @@ export const findMatches = (text: string, regexp: RegExp) => {

regexp.lastIndex = 0;
return results;
};
}

/**
* A utility function to clean up the Operating System name.
Expand All @@ -520,7 +520,7 @@ export const findMatches = (text: string, regexp: RegExp) => {
*
* @public
*/
export const cleanupOS = (os: string, pattern?: string, label?: string) => {
export function cleanupOS(os: string, pattern?: string, label?: string) {
if (pattern && label) {
os = os.replace(new RegExp(pattern, 'i'), label);
}
Expand All @@ -543,21 +543,21 @@ export const cleanupOS = (os: string, pattern?: string, label?: string) => {
);

return value;
};
}

/**
* A utility function to check whether the current browser is running on the
* android platform.
* @public
*/
export const isAndroidOS = () => {
export function isAndroidOS() {
const ua = navigator.userAgent;
const match = new RegExp('\\b' + 'Android' + '(?:/[\\d.]+|[ \\w.]*)', 'i').exec(ua);
if (!match) {
return false;
}
return cleanupOS(match[0], 'Android', 'Android').includes('Android');
};
}

/**
* Generate a random float between min and max. If only one parameter is
Expand All @@ -568,13 +568,14 @@ export const isAndroidOS = () => {
*
* @public
*/
export const randomFloat = (min: number, max?: number) => {
export function randomFloat(min: number, max?: number) {
if (!max) {
max = min;
min = 0;
}

return Math.random() * (max - min + 1) + min;
};
}

/**
* Generate a random integer between min and max. If only one parameter is
Expand All @@ -585,7 +586,9 @@ export const randomFloat = (min: number, max?: number) => {
*
* @public
*/
export const randomInt = (min: number, max?: number) => Math.floor(randomFloat(min, max));
export function randomInt(min: number, max?: number) {
return Math.floor(randomFloat(min, max));
}

/**
* Converts a string, including strings in camelCase or snake_case, into Start
Expand All @@ -598,12 +601,12 @@ export const randomInt = (min: number, max?: number) => Math.floor(randomFloat(m
*
* @param str - the string to examine
*/
export const startCase = (string: string) => {
export function startCase(string: string) {
return string
.replace(/_/g, ' ')
.replace(/([a-z])([A-Z])/g, (_, $1: string, $2: string) => `${$1} ${$2}`)
.replace(/(\s|^)(\w)/g, (_, $1: string, $2: string) => `${$1}${$2.toUpperCase()}`);
};
}

const wordSeparators = /[\s!"#$%&'()*+,./:;<=>?@[\\\]^_`{|}~\u2000-\u206F\u2E00-\u2E7F\-]+/;
const capitals = /[A-Z\u00C0-\u00D6\u00D9-\u00DD]/g;
Expand All @@ -622,7 +625,7 @@ const capitals = /[A-Z\u00C0-\u00D6\u00D9-\u00DD]/g;
* brown# fox'); // 'the-quick-brown-fox' kebabCase('theQUICKBrownFox'); //
* 'the-q-u-i-c-k-brown-fox'
*/
export const kebabCase = (string: string) => {
export function kebabCase(string: string) {
// replace capitals with space + lower case equivalent for later parsing
return string
.replace(capitals, (match) => {
Expand All @@ -631,7 +634,7 @@ export const kebabCase = (string: string) => {
.trim()
.split(wordSeparators)
.join('-');
};
}

interface UniqueIdParameter {
/**
Expand All @@ -657,9 +660,9 @@ interface UniqueIdParameter {
*
* @public
*/
export const uniqueId = ({ prefix = '', size }: UniqueIdParameter = { prefix: '' }) => {
export function uniqueId({ prefix = '', size }: UniqueIdParameter = { prefix: '' }) {
return `${prefix}${nanoid(size)}`;
};
}

/**
* Takes a number of elements from the provided array starting from the
Expand All @@ -670,17 +673,19 @@ export const uniqueId = ({ prefix = '', size }: UniqueIdParameter = { prefix: ''
*
* @public
*/
export const take = <GArray extends any[]>(array: GArray, number: number) => {
export function take<GArray extends any[]>(array: GArray, number: number) {
number = Math.max(Math.min(0, number), number);
return array.slice(0, number);
};
}

/**
* Alias for excluding properties from an object
*/
export const omit = objectOmit;

export const omitUndefined = (object: PlainObject) => omit(object, (value) => !isUndefined(value));
export function omitUndefined(object: PlainObject) {
return omit(object, (value) => !isUndefined(value));
}

/**
* Clones a plain object using object spread notation
Expand All @@ -689,12 +694,25 @@ export const omitUndefined = (object: PlainObject) => omit(object, (value) => !i
*
* @public
*/
export const clone = <GObject extends object>(value: GObject) => {
export function clone<Type extends object>(value: Type): Type {
if (!isPlainObject(value)) {
throw new Error('An invalid value was passed into this clone utility. Expected a plain object');
}

return { ...value };
};
}

/**
* Shallow clone an object while preserving it's getters and setters. This is a
* an alternative to the spread clone.
*/
export function shallowClone<Type extends object>(value: Type): Type {
const clone = Object.create(Object.getPrototypeOf(value));
const descriptors = Object.getOwnPropertyDescriptors(value);
Object.defineProperties(clone, descriptors);

return clone;
}

/**
* Alias for fast deep equal
Expand Down
16 changes: 8 additions & 8 deletions @remirror/core/src/builtins/suggestion-extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,29 @@ const SuggestionExtension = ExtensionFactory.plain({
* Ensure that all ssr transformers are run.
*/
onInitialize({ getParameter, addPlugins, managerSettings }) {
const suggestions: Suggestion[] = [];
const suggesters: Suggestion[] = [];

return {
forEachExtension: (extension) => {
if (
// Manager settings excluded this from running
managerSettings.exclude?.suggestions ||
managerSettings.exclude?.suggesters ||
// Method doesn't exist
!extension.parameter.createSuggestions ||
// Extension settings exclude it from running
extension.settings.exclude.suggestions
extension.settings.exclude.suggesters
) {
return;
}

const parameter = getParameter(extension);
const suggester = extension.parameter.createSuggestions(parameter, extension);

suggestions.push(...(isArray(suggester) ? suggester : [suggester]));
suggesters.push(...(isArray(suggester) ? suggester : [suggester]));
},

afterExtensionLoop: () => {
addPlugins(suggest(...suggestions));
addPlugins(suggest(...suggesters));
},
};
},
Expand All @@ -55,11 +55,11 @@ declare global {
namespace Remirror {
interface ExcludeOptions {
/**
* Whether to exclude the suggestions plugin configuration for the extension.
* Whether to exclude the suggesters plugin configuration for the extension.
*
* @defaultValue `undefined`
*/
suggestions?: boolean;
suggesters?: boolean;
}

interface ExtensionCreatorMethods<
Expand All @@ -71,7 +71,7 @@ declare global {
ProsemirrorType = never
> {
/**
* Create suggestions which respond to character key combinations within the
* Create suggesters which respond to character key combinations within the
* editor instance.
*
* @remarks
Expand Down
4 changes: 2 additions & 2 deletions @remirror/core/src/manager/manager-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ export function transformCommands<ExtensionUnion extends AnyExtension>(
}: {
command: ExtensionCommandFunction;
shouldDispatch?: boolean;
}) => (...spread: unknown[]) => {
}) => (...args: unknown[]) => {
let dispatch: DispatchFunction | undefined = undefined;

if (shouldDispatch) {
dispatch = view.dispatch;
view.focus(); // TODO should this be configurable?
}

return command(...spread)({ state: getState(), dispatch, view });
return command(...args)({ state: getState(), dispatch, view });
};

const chainedFactory = (command: ExtensionCommandFunction) => (...spread: unknown[]) => {
Expand Down

0 comments on commit fde15ac

Please sign in to comment.