UNPKG

@wordpress/blocks

Version:
8 lines (7 loc) 14.8 kB
{ "version": 3, "sources": ["../../../src/api/parser/index.js"], "sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { parse as grammarParse } from '@wordpress/block-serialization-default-parser';\nimport { autop } from '@wordpress/autop';\n\n/**\n * Internal dependencies\n */\nimport {\n\tgetFreeformContentHandlerName,\n\tgetUnregisteredTypeHandlerName,\n\tgetBlockType,\n} from '../registration';\nimport { getSaveContent } from '../serializer';\nimport { validateBlock } from '../validation';\nimport { createBlock } from '../factory';\nimport { convertLegacyBlockNameAndAttributes } from './convert-legacy-block';\nimport { serializeRawBlock } from './serialize-raw-block';\nimport { getBlockAttributes } from './get-block-attributes';\nimport { applyBlockDeprecatedVersions } from './apply-block-deprecated-versions';\nimport { applyBuiltInValidationFixes } from './apply-built-in-validation-fixes';\n\n/**\n * The raw structure of a block includes its attributes, inner\n * blocks, and inner HTML. It is important to distinguish inner blocks from\n * the HTML content of the block as only the latter is relevant for block\n * validation and edit operations.\n *\n * @typedef WPRawBlock\n *\n * @property {string=} blockName Block name\n * @property {Object=} attrs Block raw or comment attributes.\n * @property {string} innerHTML HTML content of the block.\n * @property {(string|null)[]} innerContent Content without inner blocks.\n * @property {WPRawBlock[]} innerBlocks Inner Blocks.\n */\n\n/**\n * Fully parsed block object.\n *\n * @typedef WPBlock\n *\n * @property {string} name Block name\n * @property {Object} attributes Block raw or comment attributes.\n * @property {WPBlock[]} innerBlocks Inner Blocks.\n * @property {string} originalContent Original content of the block before validation fixes.\n * @property {boolean} isValid Whether the block is valid.\n * @property {Object[]} validationIssues Validation issues.\n * @property {WPRawBlock} [__unstableBlockSource] Un-processed original copy of block if created through parser.\n */\n\n/**\n * @typedef {Object} ParseOptions\n * @property {boolean?} __unstableSkipMigrationLogs If a block is migrated from a deprecated version, skip logging the migration details.\n * @property {boolean?} __unstableSkipAutop Whether to skip autop when processing freeform content.\n */\n\n/**\n * Convert legacy blocks to their canonical form. This function is used\n * both in the parser level for previous content and to convert such blocks\n * used in Custom Post Types templates.\n *\n * @param {WPRawBlock} rawBlock\n *\n * @return {WPRawBlock} The block's name and attributes, changed accordingly if a match was found\n */\nfunction convertLegacyBlocks( rawBlock ) {\n\tconst [ correctName, correctedAttributes ] =\n\t\tconvertLegacyBlockNameAndAttributes(\n\t\t\trawBlock.blockName,\n\t\t\trawBlock.attrs\n\t\t);\n\treturn {\n\t\t...rawBlock,\n\t\tblockName: correctName,\n\t\tattrs: correctedAttributes,\n\t};\n}\n\n/**\n * Normalize the raw block by applying the fallback block name if none given,\n * sanitize the parsed HTML...\n *\n * @param {WPRawBlock} rawBlock The raw block object.\n * @param {ParseOptions?} options Extra options for handling block parsing.\n *\n * @return {WPRawBlock} The normalized block object.\n */\nexport function normalizeRawBlock( rawBlock, options ) {\n\tconst fallbackBlockName = getFreeformContentHandlerName();\n\n\t// If the grammar parsing don't produce any block name, use the freeform block.\n\tconst rawBlockName = rawBlock.blockName || getFreeformContentHandlerName();\n\tconst rawAttributes = rawBlock.attrs || {};\n\tconst rawInnerBlocks = rawBlock.innerBlocks || [];\n\tlet rawInnerHTML = rawBlock.innerHTML.trim();\n\n\t// Fallback content may be upgraded from classic content expecting implicit\n\t// automatic paragraphs, so preserve them. Assumes wpautop is idempotent,\n\t// meaning there are no negative consequences to repeated autop calls.\n\tif (\n\t\trawBlockName === fallbackBlockName &&\n\t\trawBlockName === 'core/freeform' &&\n\t\t! options?.__unstableSkipAutop\n\t) {\n\t\trawInnerHTML = autop( rawInnerHTML ).trim();\n\t}\n\n\treturn {\n\t\t...rawBlock,\n\t\tblockName: rawBlockName,\n\t\tattrs: rawAttributes,\n\t\tinnerHTML: rawInnerHTML,\n\t\tinnerBlocks: rawInnerBlocks,\n\t};\n}\n\n/**\n * Uses the \"unregistered blockType\" to create a block object.\n *\n * @param {WPRawBlock} rawBlock block.\n *\n * @return {WPRawBlock} The unregistered block object.\n */\nfunction createMissingBlockType( rawBlock ) {\n\tconst unregisteredFallbackBlock =\n\t\tgetUnregisteredTypeHandlerName() || getFreeformContentHandlerName();\n\n\t// Preserve undelimited content for use by the unregistered type\n\t// handler. A block node's `innerHTML` isn't enough, as that field only\n\t// carries the block's own HTML and not its nested blocks.\n\tconst originalUndelimitedContent = serializeRawBlock( rawBlock, {\n\t\tisCommentDelimited: false,\n\t} );\n\n\t// Preserve full block content for use by the unregistered type\n\t// handler, block boundaries included.\n\tconst originalContent = serializeRawBlock( rawBlock, {\n\t\tisCommentDelimited: true,\n\t} );\n\n\treturn {\n\t\tblockName: unregisteredFallbackBlock,\n\t\tattrs: {\n\t\t\toriginalName: rawBlock.blockName,\n\t\t\toriginalContent,\n\t\t\toriginalUndelimitedContent,\n\t\t},\n\t\tinnerHTML: rawBlock.blockName ? originalContent : rawBlock.innerHTML,\n\t\tinnerBlocks: rawBlock.innerBlocks,\n\t\tinnerContent: rawBlock.innerContent,\n\t};\n}\n\n/**\n * Validates a block and wraps with validation meta.\n *\n * The name here is regrettable but `validateBlock` is already taken.\n *\n * @param {WPBlock} unvalidatedBlock\n * @param {import('../registration').WPBlockType} blockType\n * @return {WPBlock} validated block, with auto-fixes if initially invalid\n */\nfunction applyBlockValidation( unvalidatedBlock, blockType ) {\n\t// Attempt to validate the block.\n\tconst [ isValid ] = validateBlock( unvalidatedBlock, blockType );\n\n\tif ( isValid ) {\n\t\treturn { ...unvalidatedBlock, isValid, validationIssues: [] };\n\t}\n\n\t// If the block is invalid, attempt some built-in fixes\n\t// like custom classNames handling.\n\tconst fixedBlock = applyBuiltInValidationFixes(\n\t\tunvalidatedBlock,\n\t\tblockType\n\t);\n\t// Attempt to validate the block once again after the built-in fixes.\n\tconst [ isFixedValid, validationIssues ] = validateBlock(\n\t\tfixedBlock,\n\t\tblockType\n\t);\n\n\treturn { ...fixedBlock, isValid: isFixedValid, validationIssues };\n}\n\n/**\n * Given a raw block returned by grammar parsing, returns a fully parsed block.\n *\n * @param {WPRawBlock} rawBlock The raw block object.\n * @param {ParseOptions} options Extra options for handling block parsing.\n *\n * @return {WPBlock | undefined} Fully parsed block.\n */\nexport function parseRawBlock( rawBlock, options ) {\n\tlet normalizedBlock = normalizeRawBlock( rawBlock, options );\n\n\t// During the lifecycle of the project, we renamed some old blocks\n\t// and transformed others to new blocks. To avoid breaking existing content,\n\t// we added this function to properly parse the old content.\n\tnormalizedBlock = convertLegacyBlocks( normalizedBlock );\n\n\t// Try finding the type for known block name.\n\tlet blockType = getBlockType( normalizedBlock.blockName );\n\n\t// If not blockType is found for the specified name, fallback to the \"unregisteredBlockType\".\n\tif ( ! blockType ) {\n\t\tnormalizedBlock = createMissingBlockType( normalizedBlock );\n\t\tblockType = getBlockType( normalizedBlock.blockName );\n\t}\n\n\t// If it's an empty freeform block or there's no blockType (no missing block handler)\n\t// Then, just ignore the block.\n\t// It might be a good idea to throw a warning here.\n\t// TODO: I'm unsure about the unregisteredFallbackBlock check,\n\t// it might ignore some dynamic unregistered third party blocks wrongly.\n\tconst isFallbackBlock =\n\t\tnormalizedBlock.blockName === getFreeformContentHandlerName() ||\n\t\tnormalizedBlock.blockName === getUnregisteredTypeHandlerName();\n\tif ( ! blockType || ( ! normalizedBlock.innerHTML && isFallbackBlock ) ) {\n\t\treturn;\n\t}\n\n\t// Parse inner blocks recursively.\n\tconst parsedInnerBlocks = normalizedBlock.innerBlocks\n\t\t.map( ( innerBlock ) => parseRawBlock( innerBlock, options ) )\n\t\t// See https://github.com/WordPress/gutenberg/pull/17164.\n\t\t.filter( ( innerBlock ) => !! innerBlock );\n\n\t// Get the fully parsed block.\n\tconst parsedBlock = createBlock(\n\t\tnormalizedBlock.blockName,\n\t\tgetBlockAttributes(\n\t\t\tblockType,\n\t\t\tnormalizedBlock.innerHTML,\n\t\t\tnormalizedBlock.attrs\n\t\t),\n\t\tparsedInnerBlocks\n\t);\n\tparsedBlock.originalContent = normalizedBlock.innerHTML;\n\n\tconst validatedBlock = applyBlockValidation( parsedBlock, blockType );\n\tconst { validationIssues } = validatedBlock;\n\n\t// Run the block deprecation and migrations.\n\t// This is performed on both invalid and valid blocks because\n\t// migration using the `migrate` functions should run even\n\t// if the output is deemed valid.\n\tconst updatedBlock = applyBlockDeprecatedVersions(\n\t\tvalidatedBlock,\n\t\tnormalizedBlock,\n\t\tblockType\n\t);\n\n\tif ( ! updatedBlock.isValid ) {\n\t\t// Preserve the original unprocessed version of the block\n\t\t// that we received (no fixes, no deprecations) so that\n\t\t// we can save it as close to exactly the same way as\n\t\t// we loaded it. This is important to avoid corruption\n\t\t// and data loss caused by block implementations trying\n\t\t// to process data that isn't fully recognized.\n\t\tupdatedBlock.__unstableBlockSource = rawBlock;\n\t}\n\n\tif (\n\t\t! validatedBlock.isValid &&\n\t\tupdatedBlock.isValid &&\n\t\t! options?.__unstableSkipMigrationLogs\n\t) {\n\t\t/* eslint-disable no-console */\n\t\tconsole.groupCollapsed( 'Updated Block: %s', blockType.name );\n\t\tconsole.info(\n\t\t\t'Block successfully updated for `%s` (%o).\\n\\nNew content generated by `save` function:\\n\\n%s\\n\\nContent retrieved from post body:\\n\\n%s',\n\t\t\tblockType.name,\n\t\t\tblockType,\n\t\t\tgetSaveContent( blockType, updatedBlock.attributes ),\n\t\t\tupdatedBlock.originalContent\n\t\t);\n\t\tconsole.groupEnd();\n\t\t/* eslint-enable no-console */\n\t} else if ( ! validatedBlock.isValid && ! updatedBlock.isValid ) {\n\t\tvalidationIssues.forEach( ( { log, args } ) => log( ...args ) );\n\t}\n\n\treturn updatedBlock;\n}\n\n/**\n * Utilizes an optimized token-driven parser based on the Gutenberg grammar spec\n * defined through a parsing expression grammar to take advantage of the regular\n * cadence provided by block delimiters -- composed syntactically through HTML\n * comments -- which, given a general HTML document as an input, returns a block\n * list array representation.\n *\n * This is a recursive-descent parser that scans linearly once through the input\n * document. Instead of directly recursing it utilizes a trampoline mechanism to\n * prevent stack overflow. This initial pass is mainly interested in separating\n * and isolating the blocks serialized in the document and manifestly not in the\n * content within the blocks.\n *\n * @see\n * https://developer.wordpress.org/block-editor/packages/packages-block-serialization-default-parser/\n *\n * @param {string} content The post content.\n * @param {ParseOptions} options Extra options for handling block parsing.\n *\n * @return {Array} Block list.\n */\nexport default function parse( content, options ) {\n\treturn grammarParse( content ).reduce( ( accumulator, rawBlock ) => {\n\t\tconst block = parseRawBlock( rawBlock, options );\n\t\tif ( block ) {\n\t\t\taccumulator.push( block );\n\t\t}\n\t\treturn accumulator;\n\t}, [] );\n}\n"], "mappings": "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", "names": ["grammarParse"] }