From 41111b1c277dda23145bc2bb619c9b92a33b044e Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Mon, 10 Sep 2018 11:23:58 -0400 Subject: [PATCH] Tooling: Improve docs build to consider memoized selectors --- docs/data/data-core-blocks.md | 38 ++++++ docs/data/data-core-edit-post.md | 12 ++ docs/data/data-core-editor.md | 210 ++++++++++++++++++++++++++++++- docs/data/data-core-nux.md | 14 +++ docs/data/data-core.md | 13 ++ docs/tool/parser.js | 107 +++++++++++++--- 6 files changed, 374 insertions(+), 20 deletions(-) diff --git a/docs/data/data-core-blocks.md b/docs/data/data-core-blocks.md index 380f23c5c1c26..808cadeff4286 100644 --- a/docs/data/data-core-blocks.md +++ b/docs/data/data-core-blocks.md @@ -2,6 +2,14 @@ ## Selectors +### getBlockTypes + +Returns all the available block types. + +*Parameters* + + * state: Data state. + ### getBlockType Returns a block type by name. @@ -11,6 +19,10 @@ Returns a block type by name. * state: Data state. * name: Block type name. +*Returns* + +Block Type. + ### getCategories Returns all the available categories. @@ -47,6 +59,32 @@ Returns the name of the fallback block name. Fallback block name. +### getChildBlockNames + +Returns an array with the child blocks of a given block. + +*Parameters* + + * state: Data state. + * blockName: Block type name. + +*Returns* + +Array of child block names. + +### hasChildBlocks + +Returns a boolean indicating if a block has child blocks or not. + +*Parameters* + + * state: Data state. + * blockName: Block type name. + +*Returns* + +True if a block contains child blocks and false otherwise. + ## Actions ### addBlockTypes diff --git a/docs/data/data-core-edit-post.md b/docs/data/data-core-edit-post.md index eb7289bfc33c8..9a5918f3a4086 100644 --- a/docs/data/data-core-edit-post.md +++ b/docs/data/data-core-edit-post.md @@ -167,6 +167,18 @@ Returns the state of legacy meta boxes. State of meta box at specified location. +### hasMetaBoxes + +Returns true if the post is using Meta Boxes + +*Parameters* + + * state: Global application state + +*Returns* + +Whether there are metaboxes or not. + ### isSavingMetaBoxes Returns true if the Meta Boxes are being saved. diff --git a/docs/data/data-core-editor.md b/docs/data/data-core-editor.md index 08e61c6fcdda0..c7d6e7dd151b6 100644 --- a/docs/data/data-core-editor.md +++ b/docs/data/data-core-editor.md @@ -320,6 +320,25 @@ unsaved status values. Whether the post has been published. +### getBlockDependantsCacheBust + +Returns a new reference when the inner blocks of a given block client ID +change. This is used exclusively as a memoized selector dependant, relying +on this selector's shared return value and recursively those of its inner +blocks defined as dependencies. This abuses mechanics of the selector +memoization to return from the original selector function only when +dependants change. + +*Parameters* + + * state: Editor state. + * clientId: Block client ID. + +*Returns* + +A value whose reference will change only when inner blocks of + the given block client ID change. + ### getBlockName Returns a block's name given its client ID, or null if no block exists with @@ -334,6 +353,80 @@ the client ID. Block name. +### getBlock + +Returns a block given its client ID. This is a parsed copy of the block, +containing its `blockName`, `clientId`, and current `attributes` state. This +is not the block's registration settings, which must be retrieved from the +blocks module registration store. + +*Parameters* + + * state: Editor state. + * clientId: Block client ID. + +*Returns* + +Parsed block object. + +### getBlocks + +Returns all block objects for the current post being edited as an array in +the order they appear in the post. + +Note: It's important to memoize this selector to avoid return a new instance +on each call + +*Parameters* + + * state: Editor state. + * rootClientId: Optional root client ID of block list. + +*Returns* + +Post blocks. + +### getClientIdsWithDescendants + +Returns an array containing the clientIds of the top-level blocks +and their descendants of any depth (for nested blocks). + +*Parameters* + + * state: Global application state. + +*Returns* + +ids of top-level and descendant blocks. + +### getGlobalBlockCount + +Returns the total number of blocks, or the total number of blocks with a specific name in a post. +The number returned includes nested blocks. + +*Parameters* + + * state: Global application state. + * blockName: Optional block name, if specified only blocks of that type will be counted. + +*Returns* + +Number of blocks in the post, or number of blocks with name equal to blockName. + +### getBlocksByClientId + +Given an array of block client IDs, returns the corresponding array of block +objects. + +*Parameters* + + * state: Editor state. + * clientIds: Client IDs for which blocks are to be returned. + +*Returns* + +Block objects. + ### getBlockCount Returns the number of blocks currently present in the post. @@ -503,6 +596,32 @@ This position is to used to position the caret properly when the selected block Selected block. +### getMultiSelectedBlockClientIds + +Returns the current multi-selection set of block client IDs, or an empty +array if there is no multi-selection. + +*Parameters* + + * state: Editor state. + +*Returns* + +Multi-selected block client IDs. + +### getMultiSelectedBlocks + +Returns the current multi-selection set of blocks, or an empty array if +there is no multi-selection. + +*Parameters* + + * state: Editor state. + +*Returns* + +Multi-selected block objects. + ### getFirstMultiSelectedBlockClientId Returns the client ID of the first block in the multi-selection set, or null @@ -558,6 +677,21 @@ false otherwise. Whether block is in multi-selection set. +### isAncestorMultiSelected + +Returns true if an ancestor of the block is multi-selected, or false +otherwise. + +*Parameters* + + * state: Editor state. + * clientId: Block client ID. + +*Returns* + +Whether an ancestor of the block is in multi-selection + set. + ### getMultiSelectedBlocksStartClientId Returns the client ID of the block which begins the multi-selection set, or @@ -855,6 +989,19 @@ default post format. Returns null if the format cannot be determined. Suggested post format. +### getEditedPostContent + +Returns the content of the post being edited, preferring raw string edit +before falling back to serialization of block state. + +*Parameters* + + * state: Global application state. + +*Returns* + +Post content. + ### getNotices Returns the user notices array. @@ -867,6 +1014,67 @@ Returns the user notices array. List of notices. +### canInsertBlockType + +Determines if the given block type is allowed to be inserted, and, if +parentClientId is provided, whether it is allowed to be nested within the +given parent. + +*Parameters* + + * state: Editor state. + * blockName: The name of the given block type, e.g. + 'core/paragraph'. + * parentClientId: The parent that the given block is to be + nested within, or null. + +*Returns* + +Whether the given block type is allowed to be inserted. + +### getInserterItems + +Determines the items that appear in the inserter. Includes both static +items (e.g. a regular block type) and dynamic items (e.g. a reusable block). + +Each item object contains what's necessary to display a button in the +inserter and handle its selection. + +The 'utility' property indicates how useful we think an item will be to the +user. There are 4 levels of utility: + +1. Blocks that are contextually useful (utility = 3) +2. Blocks that have been previously inserted (utility = 2) +3. Blocks that are in the common category (utility = 1) +4. All other blocks (utility = 0) + +The 'frecency' property is a heuristic (https://en.wikipedia.org/wiki/Frecency) +that combines block usage frequenty and recency. + +Items are returned ordered descendingly by their 'utility' and 'frecency'. + +*Parameters* + + * state: Editor state. + * parentClientId: The block we are inserting into, if any. + +*Returns* + +Items that appear in inserter. + +### getReusableBlock + +Returns the reusable block with the given ID. + +*Parameters* + + * state: Global application state. + * ref: The reusable block's ID. + +*Returns* + +The reusable block, or null if none exists. + ### isSavingReusableBlock Returns whether or not the reusable block with the given ID is being saved. @@ -1411,4 +1619,4 @@ Returns an action object used in signalling that the editor settings have been u *Parameters* - * settings: Updated settings + * settings: Updated settings \ No newline at end of file diff --git a/docs/data/data-core-nux.md b/docs/data/data-core-nux.md index bc0446d3912ac..51f1219f994a9 100644 --- a/docs/data/data-core-nux.md +++ b/docs/data/data-core-nux.md @@ -2,6 +2,16 @@ ## Selectors +### getAssociatedGuide + +Returns an object describing the guide, if any, that the given tip is a part +of. + +*Parameters* + + * state: Global application state. + * tipId: The tip to query. + ### isTipVisible Determines whether or not the given tip is showing. Tips are hidden if they @@ -13,6 +23,10 @@ guide that they have been added to. * state: Global application state. * tipId: The tip to query. +*Returns* + +Whether or not the given tip is showing. + ### areTipsEnabled Returns whether or not tips are globally enabled. diff --git a/docs/data/data-core.md b/docs/data/data-core.md index be3d99f4a1a6f..0fe8caa27d6f2 100644 --- a/docs/data/data-core.md +++ b/docs/data/data-core.md @@ -24,6 +24,19 @@ Returns all available authors. Authors list. +### getUserQueryResults + +Returns all the users returned by a query ID. + +*Parameters* + + * state: Data state. + * queryID: Query ID. + +*Returns* + +Users list. + ### getEntitiesByKind Returns whether the entities for the give kind are loaded. diff --git a/docs/tool/parser.js b/docs/tool/parser.js index 234af0b252a8d..480e23ffc5702 100644 --- a/docs/tool/parser.js +++ b/docs/tool/parser.js @@ -6,10 +6,71 @@ const fs = require( 'fs' ); /** * External dependencies */ -const { last } = require( 'lodash' ); +const { last, size, first } = require( 'lodash' ); const espree = require( 'espree' ); const doctrine = require( 'doctrine' ); +/** + * Returns true if the given Espree parse result node is a documented named + * export declaration, or false otherwise. + * + * @param {Object} node Node to test. + * + * @return {boolean} Whether node is a documented named export declaration. + */ +function isDocumentedNamedExport( node ) { + return ( + node.type === 'ExportNamedDeclaration' && + size( node.leadingComments ) > 0 + ); +} + +/** + * Returns the assigned name for a given declaration node type, or undefined if + * it cannot be determined. + * + * @param {Object} declaration Declaration node. + * + * @return {?string} Exported declaration name. + */ +function getDeclarationExportedName( declaration ) { + let declarator; + switch ( declaration.type ) { + case 'FunctionDeclaration': + declarator = declaration; + break; + + case 'VariableDeclaration': + declarator = first( declaration.declarations ); + } + + if ( declarator ) { + return declarator.id.name; + } +} + +/** + * Maps parse type to specific filtering logic by which to consider for + * inclusion a parsed named export. + * + * @type {Object} + */ +const FILTER_PARSED_DOCBLOCK_BY_TYPE = { + /** + * Selectors filter. Excludes documented exports which do not include at + * least one `@param` DocBlock tag. This is used to distinguish between + * selectors (which at least receive state as an argument) and exported + * constant values. + * + * @param {Object} docBlock DocBlock object to test. + * + * @return {boolean} Whether documented selector should be included. + */ + selectors( docBlock ) { + return !! docBlock.tags.some( ( tag ) => tag.title === 'param' ); + }, +}; + module.exports = function( config ) { const result = {}; Object.entries( config ).forEach( ( [ namespace, namespaceConfig ] ) => { @@ -31,25 +92,33 @@ module.exports = function( config ) { } ); parsedCode.body.forEach( ( node ) => { - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration.type === 'FunctionDeclaration' && - node.leadingComments && - node.leadingComments.length !== 0 - ) { - const docBlock = doctrine.parse( - last( node.leadingComments ).value, - { unwrap: true, recoverable: true } - ); - const func = { - name: node.declaration.id.name, - description: docBlock.description, - params: docBlock.tags.filter( ( tag ) => tag.title === 'param' ), - return: docBlock.tags.find( ( tag ) => tag.title === 'return' ), - }; - - namespaceResult[ type ].push( func ); + if ( ! isDocumentedNamedExport( node ) ) { + return; + } + + const docBlock = doctrine.parse( + last( node.leadingComments ).value, + { unwrap: true, recoverable: true } + ); + + const filter = FILTER_PARSED_DOCBLOCK_BY_TYPE[ type ]; + if ( typeof filter === 'function' && ! filter( docBlock ) ) { + return; } + + const name = getDeclarationExportedName( node.declaration ); + if ( ! name ) { + return; + } + + const func = { + name, + description: docBlock.description, + params: docBlock.tags.filter( ( tag ) => tag.title === 'param' ), + return: docBlock.tags.find( ( tag ) => tag.title === 'return' ), + }; + + namespaceResult[ type ].push( func ); } ); } ); } );