From 40871fe54f0dafa81a48d39e85a1cf93dcfcb4db Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sat, 8 Jul 2023 15:32:55 +0200 Subject: [PATCH] Change API to accept only a tuple, or list of tuples --- index.js | 3 +- lib/index.js | 87 ++++++---------------- readme.md | 39 ++-------- test.js | 199 ++++++++++++++++++++++++++++----------------------- 4 files changed, 139 insertions(+), 189 deletions(-) diff --git a/index.js b/index.js index e79ed04..af9ac74 100644 --- a/index.js +++ b/index.js @@ -4,9 +4,8 @@ * @typedef {import('./lib/index.js').Find} Find * @typedef {import('./lib/index.js').Replace} Replace * @typedef {import('./lib/index.js').ReplaceFunction} ReplaceFunction - * @typedef {import('./lib/index.js').FindAndReplaceTuple} FindAndReplaceTuple - * @typedef {import('./lib/index.js').FindAndReplaceSchema} FindAndReplaceSchema * @typedef {import('./lib/index.js').FindAndReplaceList} FindAndReplaceList + * @typedef {import('./lib/index.js').FindAndReplaceTuple} FindAndReplaceTuple */ export {findAndReplace} from './lib/index.js' diff --git a/lib/index.js b/lib/index.js index 6191e3e..8c55a96 100644 --- a/lib/index.js +++ b/lib/index.js @@ -26,13 +26,10 @@ * @typedef {Array} FindAndReplaceList * Several find and replaces, in array form. * - * @typedef {Record} FindAndReplaceSchema - * Several find and replaces, in object form. - * - * @typedef {[Find, Replace]} FindAndReplaceTuple + * @typedef {[Find, Replace?]} FindAndReplaceTuple * Find and replace in tuple form. * - * @typedef {ReplaceFunction | string} Replace + * @typedef {ReplaceFunction | string | null | undefined} Replace * Thing to replace with. * * @callback ReplaceFunction @@ -67,8 +64,6 @@ import escape from 'escape-string-regexp' import {visitParents} from 'unist-util-visit-parents' import {convert} from 'unist-util-is' -const own = {}.hasOwnProperty - /** * Find patterns in a tree and replace them. * @@ -76,53 +71,19 @@ const own = {}.hasOwnProperty * nodes. * Partial matches are not supported. * - * @overload - * @param {Nodes} tree - * @param {Find} find - * @param {Replace | null | undefined} [replace] - * @param {Options | null | undefined} [options] - * @returns {undefined} - * - * @overload - * @param {Nodes} tree - * @param {FindAndReplaceSchema | FindAndReplaceList} schema - * @param {Options | null | undefined} [options] - * @returns {undefined} - * * @param {Nodes} tree * Tree to change. - * @param {Find | FindAndReplaceList | FindAndReplaceSchema} find + * @param {FindAndReplaceList | FindAndReplaceTuple} list * Patterns to find. - * @param {Options | Replace | null | undefined} [replace] - * Things to replace with (when `find` is `Find`) or configuration. * @param {Options | null | undefined} [options] * Configuration (when `find` is not `Find`). * @returns {undefined} * Nothing. */ -// To do: next major: remove `find` & `replace` combo, remove schema. -export function findAndReplace(tree, find, replace, options) { - /** @type {Options | null | undefined} */ - let settings - /** @type {FindAndReplaceList | FindAndReplaceSchema} */ - let schema - - if (typeof find === 'string' || find instanceof RegExp) { - // @ts-expect-error don’t expect options twice. - schema = [[find, replace]] - settings = options - } else { - schema = find - // @ts-expect-error don’t expect replace twice. - settings = replace - } - - if (!settings) { - settings = {} - } - +export function findAndReplace(tree, list, options) { + const settings = options || {} const ignored = convert(settings.ignore || []) - const pairs = toPairs(schema) + const pairs = toPairs(list) let pairIndex = -1 while (++pairIndex < pairs.length) { @@ -239,39 +200,33 @@ export function findAndReplace(tree, find, replace, options) { } /** - * Turn a schema into pairs. + * Turn a tuple or a list of tuples into pairs. * - * @param {FindAndReplaceList | FindAndReplaceSchema} schema + * @param {FindAndReplaceList | FindAndReplaceTuple} tupleOrList * Schema. * @returns {Pairs} * Clean pairs. */ -function toPairs(schema) { +function toPairs(tupleOrList) { /** @type {Pairs} */ const result = [] - if (typeof schema !== 'object') { - throw new TypeError('Expected array or object as schema') + if (!Array.isArray(tupleOrList)) { + throw new TypeError('Expected find and replace tuple or list of tuples') } - if (Array.isArray(schema)) { - let index = -1 + /** @type {FindAndReplaceList} */ + // @ts-expect-error: correct. + const list = + !tupleOrList[0] || Array.isArray(tupleOrList[0]) + ? tupleOrList + : [tupleOrList] - while (++index < schema.length) { - result.push([ - toExpression(schema[index][0]), - toFunction(schema[index][1]) - ]) - } - } else { - /** @type {string} */ - let key + let index = -1 - for (key in schema) { - if (own.call(schema, key)) { - result.push([toExpression(key), toFunction(schema[key])]) - } - } + while (++index < list.length) { + const tuple = list[index] + result.push([toExpression(tuple[0]), toFunction(tuple[1])]) } return result diff --git a/readme.md b/readme.md index f0d85f8..c9ce8d6 100644 --- a/readme.md +++ b/readme.md @@ -17,10 +17,9 @@ * [Install](#install) * [Use](#use) * [API](#api) - * [`findAndReplace(tree, find, replace[, options])`](#findandreplacetree-find-replace-options) + * [`findAndReplace(tree, list[, options])`](#findandreplacetree-list-options) * [`Find`](#find) * [`FindAndReplaceList`](#findandreplacelist) - * [`FindAndReplaceSchema`](#findandreplaceschema) * [`FindAndReplaceTuple`](#findandreplacetuple) * [`Options`](#options) * [`RegExpMatchObject`](#regexpmatchobject) @@ -123,7 +122,7 @@ paragraph[8] This package exports the identifier [`findAndReplace`][api-findandreplace]. There is no default export. -### `findAndReplace(tree, find, replace[, options])` +### `findAndReplace(tree, list[, options])` Find patterns in a tree and replace them. @@ -131,22 +130,13 @@ The algorithm searches the tree in *[preorder][]* for complete values in [`Text`][text] nodes. Partial matches are not supported. -###### Signatures - -* `findAndReplace(tree, find, replace[, options])` -* `findAndReplace(tree, search[, options])` - ###### Parameters * `tree` ([`Node`][node]) — tree to change -* `find` ([`Find`][api-find]) - — value to find and remove -* `replace` ([`Replace`][api-replace]) - — thing to replace with -* `search` ([`FindAndReplaceSchema`][api-findandreplaceschema] or - [`FindAndReplaceList`][api-findandreplacelist]) - — several find and replaces +* `list` ([`FindAndReplaceList`][api-findandreplacelist] or + [`FindAndReplaceTuple`][api-findandreplacetuple]) + — one or more find-and-replace pairs * `options` ([`Options`][api-options]) — configuration @@ -178,18 +168,6 @@ type FindAndReplaceList = Array See [`FindAndReplaceTuple`][api-findandreplacetuple]. -### `FindAndReplaceSchema` - -Several find and replaces, in object form (TypeScript type). - -###### Type - -```ts -type FindAndReplaceSchema = Record -``` - -See [`Replace`][api-replace]. - ### `FindAndReplaceTuple` Find and replace in tuple form (TypeScript type). @@ -197,7 +175,7 @@ Find and replace in tuple form (TypeScript type). ###### Type ```ts -type FindAndReplaceTuple = [Find, Replace] +type FindAndReplaceTuple = [Find, Replace?] ``` See [`Find`][api-find] and [`Replace`][api-replace]. @@ -265,7 +243,6 @@ Thing to replace with: This package is fully typed with [TypeScript][]. It exports the additional types [`Find`][api-find], [`FindAndReplaceList`][api-findandreplacelist], -[`FindAndReplaceSchema`][api-findandreplaceschema], [`FindAndReplaceTuple`][api-findandreplacetuple], [`Options`][api-options], [`RegExpMatchObject`][api-regexpmatchobject], @@ -371,7 +348,7 @@ abide by its terms. [hast-util-find-and-replace]: https://github.com/syntax-tree/hast-util-find-and-replace -[api-findandreplace]: #findandreplacetree-find-replace-options +[api-findandreplace]: #findandreplacetree-list-options [api-options]: #options @@ -383,8 +360,6 @@ abide by its terms. [api-findandreplacelist]: #findandreplacelist -[api-findandreplaceschema]: #findandreplaceschema - [api-findandreplacetuple]: #findandreplacetuple [api-regexpmatchobject]: #regexpmatchobject diff --git a/test.js b/test.js index ace42c2..25b0b15 100644 --- a/test.js +++ b/test.js @@ -16,13 +16,15 @@ test('findAndReplace', async function (t) { assert.throws(function () { // @ts-expect-error: check that the runtime throws an error. findAndReplace(create(), true) - }, /^TypeError: Expected array or object as schema$/) + }, /Expected find and replace tuple or list of tuples/) } ) await t.test('should remove without `replace`', async function () { const tree = create() - findAndReplace(tree, 'emphasis') + + findAndReplace(tree, ['emphasis']) + assert.deepEqual( tree, u('paragraph', [ @@ -38,10 +40,10 @@ test('findAndReplace', async function (t) { }) await t.test( - 'should work when given `find` and `replace`', + 'should work when given a find-and-replace tuple', async function () { const tree = create() - findAndReplace(tree, 'emphasis', '!!!') + findAndReplace(tree, ['emphasis', '!!!']) assert.deepEqual( tree, u('paragraph', [ @@ -62,13 +64,12 @@ test('findAndReplace', async function (t) { async function () { const tree = create() - findAndReplace( - tree, + findAndReplace(tree, [ /em(\w+)is/, function (/** @type {string} */ _, /** @type {string} */ $1) { return '[' + $1 + ']' } - ) + ]) assert.deepEqual( tree, @@ -90,9 +91,12 @@ test('findAndReplace', async function (t) { async function () { const tree = create() - findAndReplace(tree, 'emphasis', function () { - return '' - }) + findAndReplace(tree, [ + 'emphasis', + function () { + return '' + } + ]) assert.deepEqual( tree, @@ -114,9 +118,12 @@ test('findAndReplace', async function (t) { async function () { const tree = create() - findAndReplace(tree, 'emphasis', function () { - return u('delete', [u('break')]) - }) + findAndReplace(tree, [ + 'emphasis', + function () { + return u('delete', [u('break')]) + } + ]) assert.deepEqual( tree, @@ -138,9 +145,12 @@ test('findAndReplace', async function (t) { async function () { const tree = create() - findAndReplace(tree, 'emphasis', function () { - return [u('delete', []), u('break')] - }) + findAndReplace(tree, [ + 'emphasis', + function () { + return [u('delete', []), u('break')] + } + ]) assert.deepEqual( tree, @@ -157,59 +167,43 @@ test('findAndReplace', async function (t) { } ) - await t.test( - 'should work when given `search` as an matrix of strings', - async function () { - const tree = create() + await t.test('should work when given a list of tuples', async function () { + const tree = create() - findAndReplace(tree, [ - ['emphasis', '!!!'], - ['importance', '???'] - ]) + findAndReplace(tree, [ + ['emphasis', '!!!'], + ['importance', '???'] + ]) - assert.deepEqual( - tree, - u('paragraph', [ - u('text', 'Some '), - u('emphasis', [u('text', '!!!')]), - u('text', ', '), - u('strong', [u('text', '???')]), - u('text', ', and '), - u('inlineCode', 'code'), - u('text', '.') - ]) - ) - } - ) + assert.deepEqual( + tree, + u('paragraph', [ + u('text', 'Some '), + u('emphasis', [u('text', '!!!')]), + u('text', ', '), + u('strong', [u('text', '???')]), + u('text', ', and '), + u('inlineCode', 'code'), + u('text', '.') + ]) + ) + }) await t.test( - 'should work when given `search` as an object of strings', + 'should work when given an empty list of tuples', async function () { const tree = create() - findAndReplace(tree, {emp: 'hacks', ',': '!'}) + findAndReplace(tree, []) - assert.deepEqual( - tree, - u('paragraph', [ - u('text', 'Some '), - u('emphasis', [u('text', 'hacks'), u('text', 'hasis')]), - u('text', '!'), - u('text', ' '), - u('strong', [u('text', 'importance')]), - u('text', '!'), - u('text', ' and '), - u('inlineCode', 'code'), - u('text', '.') - ]) - ) + assert.deepEqual(tree, create()) } ) await t.test('should work on partial matches', async function () { const tree = create() - findAndReplace(tree, /\Bmp\B/, '[MP]') + findAndReplace(tree, [/\Bmp\B/, '[MP]']) assert.deepEqual( tree, @@ -228,12 +222,15 @@ test('findAndReplace', async function (t) { await t.test('should find-and-replace recursively', async function () { const tree = create() - findAndReplace(tree, { - emphasis() { - return u('link', {url: 'x'}, [u('text', 'importance')]) - }, - importance: 'something else' - }) + findAndReplace(tree, [ + [ + 'emphasis', + function () { + return u('link', {url: 'x'}, [u('text', 'importance')]) + } + ], + ['importance', 'something else'] + ]) assert.deepEqual( tree, @@ -259,7 +256,7 @@ test('findAndReplace', async function (t) { u('text', '.') ]) - findAndReplace(tree, 'importance', '!!!', {ignore: 'strong'}) + findAndReplace(tree, ['importance', '!!!'], {ignore: 'strong'}) assert.deepEqual( tree, @@ -278,17 +275,26 @@ test('findAndReplace', async function (t) { u('text', 'Some emphasis, importance, and code.') ]) - findAndReplace(tree, { - importance(/** @type {string} */ value) { - return u('strong', [u('text', value)]) - }, - code(/** @type {string} */ value) { - return u('inlineCode', value) - }, - emphasis(/** @type {string} */ value) { - return u('emphasis', [u('text', value)]) - } - }) + findAndReplace(tree, [ + [ + 'importance', + function (/** @type {string} */ value) { + return u('strong', [u('text', value)]) + } + ], + [ + 'code', + function (/** @type {string} */ value) { + return u('inlineCode', value) + } + ], + [ + 'emphasis', + function (/** @type {string} */ value) { + return u('emphasis', [u('text', value)]) + } + ] + ]) assert.deepEqual(tree, create()) }) @@ -346,9 +352,12 @@ test('findAndReplace', async function (t) { await t.test('should not replace when returning false', async function () { const tree = create() - findAndReplace(tree, 'emphasis', function () { - return false - }) + findAndReplace(tree, [ + 'emphasis', + function () { + return false + } + ]) assert.deepEqual( tree, @@ -367,9 +376,12 @@ test('findAndReplace', async function (t) { await t.test('should not recurse into a replaced value', async function () { const tree = u('paragraph', [u('text', 'asd.')]) - findAndReplace(tree, 'asd', function (/** @type {string} */ d) { - return d - }) + findAndReplace(tree, [ + 'asd', + function (/** @type {string} */ d) { + return d + } + ]) assert.deepEqual(tree, u('paragraph', [u('text', 'asd'), u('text', '.')])) }) @@ -379,9 +391,12 @@ test('findAndReplace', async function (t) { async function () { const tree = u('paragraph', [u('text', 'asd.')]) - findAndReplace(tree, 'asd', function (/** @type {string} */ d) { - return u('emphasis', [u('text', d)]) - }) + findAndReplace(tree, [ + 'asd', + function (/** @type {string} */ d) { + return u('emphasis', [u('text', d)]) + } + ]) assert.deepEqual( tree, @@ -395,9 +410,12 @@ test('findAndReplace', async function (t) { async function () { const tree = u('paragraph', [u('text', '.asd')]) - findAndReplace(tree, 'asd', function (/** @type {string} */ d) { - return u('emphasis', [u('text', d)]) - }) + findAndReplace(tree, [ + 'asd', + function (/** @type {string} */ d) { + return u('emphasis', [u('text', d)]) + } + ]) assert.deepEqual( tree, @@ -411,9 +429,12 @@ test('findAndReplace', async function (t) { async function () { const tree = u('paragraph', [u('text', 'asd')]) - findAndReplace(tree, 'asd', function (/** @type {string} */ d) { - return u('emphasis', [u('text', d)]) - }) + findAndReplace(tree, [ + 'asd', + function (/** @type {string} */ d) { + return u('emphasis', [u('text', d)]) + } + ]) assert.deepEqual( tree, @@ -425,7 +446,7 @@ test('findAndReplace', async function (t) { await t.test('security: replacer as string (safe)', async function () { const tree = create() - findAndReplace(tree, 'and', 'alert(1)') + findAndReplace(tree, ['and', 'alert(1)']) assert.deepEqual( tree,