diff --git a/packages/wordcount/README.md b/packages/wordcount/README.md index edc6ece743369..9ffff147f474c 100644 --- a/packages/wordcount/README.md +++ b/packages/wordcount/README.md @@ -30,8 +30,8 @@ const numberOfWords = count( 'Words to count', 'words', {} ) _Parameters_ - _text_ `string`: The text being processed -- _type_ `string`: The type of count. Accepts ;words', 'characters_excluding_spaces', or 'characters_including_spaces'. -- _userSettings_ `Object`: Custom settings object. +- _type_ `WPWordCountStrategy`: The type of count. Accepts 'words', 'characters_excluding_spaces', or 'characters_including_spaces'. +- _userSettings_ `WPWordCountUserSettings`: Custom settings object. _Returns_ diff --git a/packages/wordcount/src/defaultSettings.js b/packages/wordcount/src/defaultSettings.js index ad50bd125efbb..9ef804bc84105 100644 --- a/packages/wordcount/src/defaultSettings.js +++ b/packages/wordcount/src/defaultSettings.js @@ -1,3 +1,40 @@ +/** @typedef {import('./index').WPWordCountStrategy} WPWordCountStrategy */ + +/** @typedef {Partial<{type: WPWordCountStrategy, shortcodes: string[]}>} WPWordCountL10n */ + +/** + * @typedef WPWordCountSettingsFields + * @property {RegExp} HTMLRegExp Regular expression that matches HTML tags + * @property {RegExp} HTMLcommentRegExp Regular expression that matches HTML comments + * @property {RegExp} spaceRegExp Regular expression that matches spaces in HTML + * @property {RegExp} HTMLEntityRegExp Regular expression that matches HTML entities + * @property {RegExp} connectorRegExp Regular expression that matches word connectors, like em-dash + * @property {RegExp} removeRegExp Regular expression that matches various characters to be removed when counting + * @property {RegExp} astralRegExp Regular expression that matches astral UTF-16 code points + * @property {RegExp} wordsRegExp Regular expression that matches words + * @property {RegExp} characters_excluding_spacesRegExp Regular expression that matches characters excluding spaces + * @property {RegExp} characters_including_spacesRegExp Regular expression that matches characters including spaces + * @property {RegExp} shortcodesRegExp Regular expression that matches WordPress shortcodes + * @property {string[]} shortcodes List of all shortcodes + * @property {WPWordCountStrategy} type Describes what and how are we counting + * @property {WPWordCountL10n} l10n Object with human translations + */ + +/** + * Lower-level settings for word counting that can be overridden. + * + * @typedef {Partial} WPWordCountUserSettings + */ + +// Disable reason: JSDoc linter doesn't seem to parse the union (`&`) correctly: https://github.com/jsdoc/jsdoc/issues/1285 +/* eslint-disable jsdoc/valid-types */ +/** + * Word counting settings that include non-optional values we set if missing + * + * @typedef {WPWordCountUserSettings & typeof defaultSettings} WPWordCountDefaultSettings + */ +/* eslint-enable jsdoc/valid-types */ + export const defaultSettings = { HTMLRegExp: /<\/?[a-z][^>]*?>/gi, HTMLcommentRegExp: //g, diff --git a/packages/wordcount/src/index.js b/packages/wordcount/src/index.js index d7f74cbd6b2b0..b8f2e3e7c75b4 100644 --- a/packages/wordcount/src/index.js +++ b/packages/wordcount/src/index.js @@ -17,18 +17,29 @@ import stripShortcodes from './stripShortcodes'; import stripSpaces from './stripSpaces'; import transposeHTMLEntitiesToCountableChars from './transposeHTMLEntitiesToCountableChars'; +/** + * @typedef {import('./defaultSettings').WPWordCountDefaultSettings} WPWordCountSettings + * @typedef {import('./defaultSettings').WPWordCountUserSettings} WPWordCountUserSettings + */ + +/** + * Possible ways of counting. + * + * @typedef {'words'|'characters_excluding_spaces'|'characters_including_spaces'} WPWordCountStrategy + */ + /** * Private function to manage the settings. * - * @param {string} type The type of count to be done. - * @param {Object} userSettings Custom settings for the count. + * @param {WPWordCountStrategy} type The type of count to be done. + * @param {WPWordCountUserSettings} userSettings Custom settings for the count. * - * @return {void|Object|*} The combined settings object to be used. + * @return {WPWordCountSettings} The combined settings object to be used. */ function loadSettings( type, userSettings ) { - const settings = extend( defaultSettings, userSettings ); + const settings = extend( {}, defaultSettings, userSettings ); - settings.shortcodes = settings.l10n.shortcodes || {}; + settings.shortcodes = settings.l10n?.shortcodes ?? []; if ( settings.shortcodes && settings.shortcodes.length ) { settings.shortcodesRegExp = new RegExp( @@ -37,7 +48,7 @@ function loadSettings( type, userSettings ) { ); } - settings.type = type || settings.l10n.type; + settings.type = type; if ( settings.type !== 'characters_excluding_spaces' && @@ -50,56 +61,56 @@ function loadSettings( type, userSettings ) { } /** - * Match the regex for the type 'words' + * Count the words in text * - * @param {string} text The text being processed - * @param {string} regex The regular expression pattern being matched - * @param {Object} settings Settings object containing regular expressions for each strip function + * @param {string} text The text being processed + * @param {RegExp} regex The regular expression pattern being matched + * @param {WPWordCountSettings} settings Settings object containing regular expressions for each strip function * - * @return {Array|{index: number, input: string}} The matched string. + * @return {number} Count of words. */ -function matchWords( text, regex, settings ) { +function countWords( text, regex, settings ) { text = flow( - stripTags.bind( this, settings ), - stripHTMLComments.bind( this, settings ), - stripShortcodes.bind( this, settings ), - stripSpaces.bind( this, settings ), - stripHTMLEntities.bind( this, settings ), - stripConnectors.bind( this, settings ), - stripRemovables.bind( this, settings ) + stripTags.bind( null, settings ), + stripHTMLComments.bind( null, settings ), + stripShortcodes.bind( null, settings ), + stripSpaces.bind( null, settings ), + stripHTMLEntities.bind( null, settings ), + stripConnectors.bind( null, settings ), + stripRemovables.bind( null, settings ) )( text ); text = text + '\n'; - return text.match( regex ); + return text.match( regex )?.length ?? 0; } /** - * Match the regex for either 'characters_excluding_spaces' or 'characters_including_spaces' + * Count the characters in text * - * @param {string} text The text being processed - * @param {string} regex The regular expression pattern being matched - * @param {Object} settings Settings object containing regular expressions for each strip function + * @param {string} text The text being processed + * @param {RegExp} regex The regular expression pattern being matched + * @param {WPWordCountSettings} settings Settings object containing regular expressions for each strip function * - * @return {Array|{index: number, input: string}} The matched string. + * @return {number} Count of characters. */ -function matchCharacters( text, regex, settings ) { +function countCharacters( text, regex, settings ) { text = flow( - stripTags.bind( this, settings ), - stripHTMLComments.bind( this, settings ), - stripShortcodes.bind( this, settings ), - stripSpaces.bind( this, settings ), - transposeAstralsToCountableChar.bind( this, settings ), - transposeHTMLEntitiesToCountableChars.bind( this, settings ) + stripTags.bind( null, settings ), + stripHTMLComments.bind( null, settings ), + stripShortcodes.bind( null, settings ), + transposeAstralsToCountableChar.bind( null, settings ), + stripSpaces.bind( null, settings ), + transposeHTMLEntitiesToCountableChars.bind( null, settings ) )( text ); text = text + '\n'; - return text.match( regex ); + return text.match( regex )?.length ?? 0; } /** * Count some words. * - * @param {string} text The text being processed - * @param {string} type The type of count. Accepts ;words', 'characters_excluding_spaces', or 'characters_including_spaces'. - * @param {Object} userSettings Custom settings object. + * @param {string} text The text being processed + * @param {WPWordCountStrategy} type The type of count. Accepts 'words', 'characters_excluding_spaces', or 'characters_including_spaces'. + * @param {WPWordCountUserSettings} userSettings Custom settings object. * * @example * ```js @@ -109,20 +120,20 @@ function matchCharacters( text, regex, settings ) { * * @return {number} The word or character count. */ - export function count( text, type, userSettings ) { - if ( '' === text ) { - return 0; - } - - if ( text ) { - const settings = loadSettings( type, userSettings ); - const matchRegExp = settings[ type + 'RegExp' ]; - const results = - 'words' === settings.type - ? matchWords( text, matchRegExp, settings ) - : matchCharacters( text, matchRegExp, settings ); - - return results ? results.length : 0; + const settings = loadSettings( type, userSettings ); + let matchRegExp; + switch ( settings.type ) { + case 'words': + matchRegExp = settings.wordsRegExp; + return countWords( text, matchRegExp, settings ); + case 'characters_including_spaces': + matchRegExp = settings.characters_including_spacesRegExp; + return countCharacters( text, matchRegExp, settings ); + case 'characters_excluding_spaces': + matchRegExp = settings.characters_excluding_spacesRegExp; + return countCharacters( text, matchRegExp, settings ); + default: + return 0; } } diff --git a/packages/wordcount/src/stripConnectors.js b/packages/wordcount/src/stripConnectors.js index 4d7a7e649c6f3..1bb84a87c2dc0 100644 --- a/packages/wordcount/src/stripConnectors.js +++ b/packages/wordcount/src/stripConnectors.js @@ -1,14 +1,11 @@ /** * Replaces items matched in the regex with spaces. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripConnectors( settings, text ) { - if ( settings.connectorRegExp ) { - return text.replace( settings.connectorRegExp, ' ' ); - } - return text; + return text.replace( settings.connectorRegExp, ' ' ); } diff --git a/packages/wordcount/src/stripHTMLComments.js b/packages/wordcount/src/stripHTMLComments.js index 3e6b11d1c822b..404f1703c51cd 100644 --- a/packages/wordcount/src/stripHTMLComments.js +++ b/packages/wordcount/src/stripHTMLComments.js @@ -1,14 +1,11 @@ /** * Removes items matched in the regex. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripHTMLComments( settings, text ) { - if ( settings.HTMLcommentRegExp ) { - return text.replace( settings.HTMLcommentRegExp, '' ); - } - return text; + return text.replace( settings.HTMLcommentRegExp, '' ); } diff --git a/packages/wordcount/src/stripHTMLEntities.js b/packages/wordcount/src/stripHTMLEntities.js index b1304fd1ef847..684b842997007 100644 --- a/packages/wordcount/src/stripHTMLEntities.js +++ b/packages/wordcount/src/stripHTMLEntities.js @@ -1,14 +1,11 @@ /** * Removes items matched in the regex. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripHTMLEntities( settings, text ) { - if ( settings.HTMLEntityRegExp ) { - return text.replace( settings.HTMLEntityRegExp, '' ); - } - return text; + return text.replace( settings.HTMLEntityRegExp, '' ); } diff --git a/packages/wordcount/src/stripRemovables.js b/packages/wordcount/src/stripRemovables.js index eec5dec8b128b..67d30e54252f6 100644 --- a/packages/wordcount/src/stripRemovables.js +++ b/packages/wordcount/src/stripRemovables.js @@ -1,14 +1,11 @@ /** * Removes items matched in the regex. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripRemovables( settings, text ) { - if ( settings.removeRegExp ) { - return text.replace( settings.removeRegExp, '' ); - } - return text; + return text.replace( settings.removeRegExp, '' ); } diff --git a/packages/wordcount/src/stripShortcodes.js b/packages/wordcount/src/stripShortcodes.js index b5d9099d49d9a..f0d82ca5ec358 100644 --- a/packages/wordcount/src/stripShortcodes.js +++ b/packages/wordcount/src/stripShortcodes.js @@ -1,8 +1,8 @@ /** * Replaces items matched in the regex with a new line. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ diff --git a/packages/wordcount/src/stripSpaces.js b/packages/wordcount/src/stripSpaces.js index f9d1c159b9272..4162a08e6825a 100644 --- a/packages/wordcount/src/stripSpaces.js +++ b/packages/wordcount/src/stripSpaces.js @@ -1,13 +1,11 @@ /** * Replaces items matched in the regex with spaces. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripSpaces( settings, text ) { - if ( settings.spaceRegExp ) { - return text.replace( settings.spaceRegExp, ' ' ); - } + return text.replace( settings.spaceRegExp, ' ' ); } diff --git a/packages/wordcount/src/stripTags.js b/packages/wordcount/src/stripTags.js index 9dcaa94c00886..25b30b6f598ad 100644 --- a/packages/wordcount/src/stripTags.js +++ b/packages/wordcount/src/stripTags.js @@ -1,13 +1,11 @@ /** * Replaces items matched in the regex with new line * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function stripTags( settings, text ) { - if ( settings.HTMLRegExp ) { - return text.replace( settings.HTMLRegExp, '\n' ); - } + return text.replace( settings.HTMLRegExp, '\n' ); } diff --git a/packages/wordcount/src/transposeAstralsToCountableChar.js b/packages/wordcount/src/transposeAstralsToCountableChar.js index af62feb8e3c5c..1b850349c0abf 100644 --- a/packages/wordcount/src/transposeAstralsToCountableChar.js +++ b/packages/wordcount/src/transposeAstralsToCountableChar.js @@ -1,14 +1,11 @@ /** * Replaces items matched in the regex with character. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ export default function transposeAstralsToCountableChar( settings, text ) { - if ( settings.astralRegExp ) { - return text.replace( settings.astralRegExp, 'a' ); - } - return text; + return text.replace( settings.astralRegExp, 'a' ); } diff --git a/packages/wordcount/src/transposeHTMLEntitiesToCountableChars.js b/packages/wordcount/src/transposeHTMLEntitiesToCountableChars.js index 90c30374e1602..f7ef0e165388f 100644 --- a/packages/wordcount/src/transposeHTMLEntitiesToCountableChars.js +++ b/packages/wordcount/src/transposeHTMLEntitiesToCountableChars.js @@ -1,8 +1,8 @@ /** * Replaces items matched in the regex with a single character. * - * @param {Object} settings The main settings object containing regular expressions - * @param {string} text The string being counted. + * @param {import('./index').WPWordCountSettings} settings The main settings object containing regular expressions + * @param {string} text The string being counted. * * @return {string} The manipulated text. */ @@ -10,8 +10,5 @@ export default function transposeHTMLEntitiesToCountableChars( settings, text ) { - if ( settings.HTMLEntityRegExp ) { - return text.replace( settings.HTMLEntityRegExp, 'a' ); - } - return text; + return text.replace( settings.HTMLEntityRegExp, 'a' ); } diff --git a/packages/wordcount/tsconfig.json b/packages/wordcount/tsconfig.json new file mode 100644 index 0000000000000..3c2c31f506f13 --- /dev/null +++ b/packages/wordcount/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "declarationDir": "build-types" + }, + "include": [ "src/**/*" ] +} diff --git a/tsconfig.json b/tsconfig.json index 05ca9f0742154..d2c47bcffdbb8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,7 +21,8 @@ { "path": "packages/project-management-automation" }, { "path": "packages/token-list" }, { "path": "packages/url" }, - { "path": "packages/warning" } + { "path": "packages/warning" }, + { "path": "packages/wordcount" } ], "files": [] }