/******/ (() => { // webpackBootstrap
/******/ // The require scope
/******/ var __webpack_require__ = {};
/************************************************************************/
/******/ /* webpack/runtime/define property getters */
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ /* webpack/runtime/make namespace object */
/******/ // define __esModule on exports
/******/ __webpack_require__.r = (exports) => {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/************************************************************************/
var __webpack_exports__ = {};
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ parse: () => (/* binding */ parse)
* @typedef {Object|null} Attributes
* @typedef {Object} ParsedBlock
* @property {string|null} blockName Block name.
* @property {Attributes} attrs Block attributes.
* @property {ParsedBlock[]} innerBlocks Inner blocks.
* @property {string} innerHTML Inner HTML.
* @property {Array<string|null>} innerContent Inner content.
* @typedef {Object} ParsedFrame
* @property {ParsedBlock} block Block.
* @property {number} tokenStart Token start.
* @property {number} tokenLength Token length.
* @property {number} prevOffset Previous offset.
* @property {number|null} leadingHtmlStart Leading HTML start.
* @typedef {'no-more-tokens'|'void-block'|'block-opener'|'block-closer'} TokenType
* @typedef {[TokenType, string, Attributes, number, number]} Token
* Matches block comment delimiters
* While most of this pattern is straightforward the attribute parsing
* incorporates a tricks to make sure we don't choke on specific input
* - since JavaScript has no possessive quantifier or atomic grouping
* we are emulating it with a trick
* we want a possessive quantifier or atomic group to prevent backtracking
* on the `}`s should we fail to match the remainder of the pattern
* we can emulate this with a positive lookahead and back reference
* (a++)*c === ((?=(a+))\1)*c
* let's examine an example:
* - /(a+)*c/.test('aaaaaaaaaaaaad') fails after over 49,000 steps
* - /(a++)*c/.test('aaaaaaaaaaaaad') fails after 85 steps
* - /(?>a+)*c/.test('aaaaaaaaaaaaad') fails after 126 steps
* this is because the possessive `++` and the atomic group `(?>)`
* tell the engine that all those `a`s belong together as a single group
* and so it won't split it up when stepping backwards to try and match
* if we use /((?=(a+))\1)*c/ then we get the same behavior as the atomic group
* or possessive and prevent the backtracking because the `a+` is matched but
* not captured. thus, we find the long string of `a`s and remember it, then
* reference it as a whole unit inside our pattern
* @see http://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead
* @see http://blog.stevenlevithan.com/archives/mimic-atomic-groups
* @see https://javascript.info/regexp-infinite-backtracking-problem
* once browsers reliably support atomic grouping or possessive
* quantifiers natively we should remove this trick and simplify
* @since 4.6.1 added optimization to prevent backtracking on attribute parsing
const tokenizer = /<!--\s+(\/)?wp:([a-z][a-z0-9_-]*\/)?([a-z][a-z0-9_-]*)\s+({(?:(?=([^}]+|}+(?=})|(?!}\s+\/?-->)[^])*)\5|[^]*?)}\s+)?(\/)?-->/g;
* Constructs a block object.
* @param {string|null} blockName
* @param {Attributes} attrs
* @param {ParsedBlock[]} innerBlocks
* @param {string} innerHTML
* @param {string[]} innerContent
* @return {ParsedBlock} The block object.
function Block(blockName, attrs, innerBlocks, innerHTML, innerContent) {
* Constructs a freeform block object.
* @param {string} innerHTML
* @return {ParsedBlock} The freeform block object.
function Freeform(innerHTML) {
return Block(null, {}, [], innerHTML, [innerHTML]);
* Constructs a frame object.
* @param {ParsedBlock} block
* @param {number} tokenStart
* @param {number} tokenLength
* @param {number} prevOffset
* @param {number|null} leadingHtmlStart
* @return {ParsedFrame} The frame object.
function Frame(block, tokenStart, tokenLength, prevOffset, leadingHtmlStart) {
prevOffset: prevOffset || tokenStart + tokenLength,
* Parser function, that converts input HTML into a block based structure.
* @param {string} doc The HTML document to parse.
* <!-- wp:columns {"columns":3} -->
* <div class="wp-block-columns has-3-columns"><!-- wp:column -->
* <div class="wp-block-column"><!-- wp:paragraph -->
* <!-- /wp:paragraph --></div>
* <div class="wp-block-column"><!-- wp:paragraph -->
* <p><strong>Middle</strong></p>
* <!-- /wp:paragraph --></div>
* <div class="wp-block-column"></div>
* <!-- /wp:column --></div>
* import { parse } from '@wordpress/block-serialization-default-parser';
* blockName: "core/columns",
* blockName: "core/column",
* blockName: "core/paragraph",
* innerHTML: "\n<p>Left</p>\n"
* innerHTML: '\n<div class="wp-block-column"></div>\n'
* blockName: "core/column",
* blockName: "core/paragraph",
* innerHTML: "\n<p><strong>Middle</strong></p>\n"
* innerHTML: '\n<div class="wp-block-column"></div>\n'
* blockName: "core/column",
* innerHTML: '\n<div class="wp-block-column"></div>\n'
* innerHTML: '\n<div class="wp-block-columns has-3-columns">\n\n\n\n</div>\n'
* @return {ParsedBlock[]} A block-based representation of the input HTML.
* Parses the next token in the input document.
* @return {boolean} Returns true when there is more tokens to parse.
const stackDepth = stack.length;
const next = nextToken();
const [tokenType, blockName, attrs, startOffset, tokenLength] = next;
// We may have some HTML soup before the next block.
const leadingHtmlStart = startOffset > offset ? offset : null;
// If not in a block then flush output.
// Otherwise we have a problem
// - treat it all as freeform text
// - assume an implicit closer (easiest when not nesting)
// For the easy case we'll assume an implicit closer.
// For the nested case where it's more difficult we'll
// have to assume that multiple closers are missing
// and so we'll collapse the whole stack piecewise.
while (0 < stack.length) {
// easy case is if we stumbled upon a void block
// in the top-level of the document.
if (null !== leadingHtmlStart) {
output.push(Freeform(document.substr(leadingHtmlStart, startOffset - leadingHtmlStart)));
output.push(Block(blockName, attrs, [], '', []));
offset = startOffset + tokenLength;
// Otherwise we found an inner block.
addInnerBlock(Block(blockName, attrs, [], '', []), startOffset, tokenLength);
offset = startOffset + tokenLength;
// Track all newly-opened blocks on the stack.
stack.push(Frame(Block(blockName, attrs, [], '', []), startOffset, tokenLength, startOffset + tokenLength, leadingHtmlStart));
offset = startOffset + tokenLength;
// If we're missing an opener we're in trouble
// - assume an implicit opener
// - assume _this_ is the opener
// - give up and close out the document.
// If we're not nesting then this is easy - close the block.
addBlockFromStack(startOffset);
offset = startOffset + tokenLength;
// Otherwise we're nested and we have to close out the current
// block and add it as a innerBlock to the parent.
const stackTop = /** @type {ParsedFrame} */stack.pop();
const html = document.substr(stackTop.prevOffset, startOffset - stackTop.prevOffset);
stackTop.block.innerHTML += html;
stackTop.block.innerContent.push(html);
stackTop.prevOffset = startOffset + tokenLength;
addInnerBlock(stackTop.block, stackTop.tokenStart, stackTop.tokenLength, startOffset + tokenLength);
offset = startOffset + tokenLength;
* Parse JSON if valid, otherwise return null
* Note that JSON coming from the block comment
* delimiters is constrained to be an object
* and cannot be things like `true` or `null`
* @param {string} input JSON input string to parse
* @return {Object|null} parsed JSON if valid
function parseJSON(input) {
return JSON.parse(input);
* Finds the next token in the document.
* @return {Token} The next matched token.
// we're using a single RegExp to tokenize the block comment delimiters
// we're also using a trick here because the only difference between a
// block opener and a block closer is the leading `/` before `wp:` (and
// a closer has no attributes). we can trap them both and process the
// match back in JavaScript to see which one it was.
const matches = tokenizer.exec(document);
// We have no more tokens.
return ['no-more-tokens', '', null, 0, 0];
const startedAt = matches.index;
const [match, closerMatch, namespaceMatch, nameMatch, attrsMatch /* Internal/unused. */,, voidMatch] = matches;
const length = match.length;
const isCloser = !!closerMatch;
const isVoid = !!voidMatch;
const namespace = namespaceMatch || 'core/';
const name = namespace + nameMatch;
const hasAttrs = !!attrsMatch;
const attrs = hasAttrs ? parseJSON(attrsMatch) : {};
// This state isn't allowed
if (isCloser && (isVoid || hasAttrs)) {
// We can ignore them since they don't hurt anything
// we may warn against this at some point or reject it.
return ['void-block', name, attrs, startedAt, length];
return ['block-closer', name, null, startedAt, length];
return ['block-opener', name, attrs, startedAt, length];
* Adds a freeform block to the output.
* @param {number} [rawLength]
function addFreeform(rawLength) {
const length = rawLength ? rawLength : document.length - offset;
output.push(Freeform(document.substr(offset, length)));
* Adds inner block to the parent block.
* @param {ParsedBlock} block
* @param {number} tokenStart
* @param {number} tokenLength
* @param {number} [lastOffset]
function addInnerBlock(block, tokenStart, tokenLength, lastOffset) {
const parent = stack[stack.length - 1];
parent.block.innerBlocks.push(block);
const html = document.substr(parent.prevOffset, tokenStart - parent.prevOffset);
parent.block.innerHTML += html;
parent.block.innerContent.push(html);
parent.block.innerContent.push(null);
parent.prevOffset = lastOffset ? lastOffset : tokenStart + tokenLength;
* Adds block from the stack to the output.
* @param {number} [endOffset]
function addBlockFromStack(endOffset) {
} = /** @type {ParsedFrame} */stack.pop();
const html = endOffset ? document.substr(prevOffset, endOffset - prevOffset) : document.substr(prevOffset);
block.innerContent.push(html);
if (null !== leadingHtmlStart) {
output.push(Freeform(document.substr(leadingHtmlStart, tokenStart - leadingHtmlStart)));
(window.wp = window.wp || {}).blockSerializationDefaultParser = __webpack_exports__;