diff --git a/dist/components/Block.js b/dist/components/Block.js index 2ec30c8..9460da7 100644 --- a/dist/components/Block.js +++ b/dist/components/Block.js @@ -1,16 +1,22 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = Block; +var _Tree = _interopRequireDefault(require("./Tree.js")); +var _innerNode = _interopRequireDefault(require("../utils/innerNode.js")); +var _Context = require("./Context.js"); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // eslint-disable-next-line no-unused-vars -import Tree from "./Tree.js"; -import innerNode from "../utils/innerNode.js"; -import { useBlockComponent } from "./Context.js"; /** * Block element. * * @param {object} componentProps - properties that includes the block object. * @returns {JSX.Element | null | undefined} - */ -import { jsx as _jsx } from "react/jsx-runtime"; -export default function Block(_ref) { + */function Block(_ref) { let { block } = _ref; @@ -19,9 +25,9 @@ export default function Block(_ref) { innerContent, innerBlocks } = block; - const CustomBlock = useBlockComponent(blockName); + const CustomBlock = (0, _Context.useBlockComponent)(blockName); if (CustomBlock) { - return /*#__PURE__*/_jsx(CustomBlock, { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomBlock, { block: block }); } @@ -34,9 +40,9 @@ export default function Block(_ref) { if (innerContent.length === 1 && (innerContent[0] === "\n" || innerContent[0].substring(0, 2) === " /*#__PURE__*/_jsx(Block, { + return block.innerBlocks?.map((inner, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_Block.default, { block: inner }, index)); } return node.data; } if (CustomTag) { - const children = node.children?.map((child, index) => /*#__PURE__*/_jsx(Tree, { + const children = node.children?.map((child, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Tree, { node: child, block: block }, index)); - return /*#__PURE__*/_jsx(CustomTag, { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(CustomTag, { attribs: node.attribs, node: children, block: block @@ -33,10 +41,10 @@ export default function Tree(_ref) { // Component is used as a dynamic tag name in JSX // eslint-disable-next-line no-unused-vars const Component = node.name; - const attrs = attribsProps(node.attribs); - return /*#__PURE__*/_jsx(Component, { + const attrs = (0, _attribsProps.default)(node.attribs); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, { ...attrs, - children: node.children?.map((child, index) => /*#__PURE__*/_jsx(Tree, { + children: node.children?.map((child, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Tree, { node: child, block: block }, index)) diff --git a/dist/elements/index.js b/dist/elements/index.js index 623042a..070b263 100644 --- a/dist/elements/index.js +++ b/dist/elements/index.js @@ -1,14 +1,23 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.customTags = exports.customBlocks = exports.coreTags = exports.coreBlocks = void 0; +var _img = _interopRequireDefault(require("./tags/img.js")); +var _selfClosing = _interopRequireDefault(require("./tags/selfClosing.js")); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // eslint-disable-next-line no-unused-vars -import Image from './tags/img.js'; + // eslint-disable-next-line no-unused-vars -import SelfClosing from './tags/selfClosing.js'; -import { jsx as _jsx } from "react/jsx-runtime"; -export const coreTags = { + +const coreTags = exports.coreTags = { img: _ref => { let { attribs } = _ref; - return /*#__PURE__*/_jsx(Image, { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_img.default, { attribs: attribs }); }, @@ -16,7 +25,7 @@ export const coreTags = { let { attribs } = _ref2; - return /*#__PURE__*/_jsx(SelfClosing, { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_selfClosing.default, { attribs: attribs, tag: "br" }); @@ -25,7 +34,7 @@ export const coreTags = { let { attribs } = _ref3; - return /*#__PURE__*/_jsx(SelfClosing, { + return /*#__PURE__*/(0, _jsxRuntime.jsx)(_selfClosing.default, { attribs: attribs, tag: "hr" }); @@ -48,7 +57,7 @@ export const coreTags = { // track: ({attribs}) => , // wbr: ({attribs}) => , }; -export const coreBlocks = { +const coreBlocks = exports.coreBlocks = { // 'core/archives': ({ block }) => doSomething(), // 'core/audio': ({ block }) => doSomething(), // 'core/avatar': ({ block }) => doSomething(), @@ -150,7 +159,7 @@ export const coreBlocks = { * @param {object} tags - Optional object with custom blocks definitions. Empty by default. * @returns {object} Object with blocks definitions */ -export const customTags = function () { +const customTags = function () { let tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { ...coreTags, @@ -165,10 +174,12 @@ export const customTags = function () { * @param {boolean} useDefaultBlocks - Optional boolean to use core blocks defaults. True by default. * @returns {object} Object with blocks definitions */ -export const customBlocks = function () { +exports.customTags = customTags; +const customBlocks = function () { let blocks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { ...coreBlocks, ...blocks }; -}; \ No newline at end of file +}; +exports.customBlocks = customBlocks; \ No newline at end of file diff --git a/dist/elements/tags/img.js b/dist/elements/tags/img.js index 6dca87e..ecc79de 100644 --- a/dist/elements/tags/img.js +++ b/dist/elements/tags/img.js @@ -1,5 +1,11 @@ -import { jsx as _jsx } from "react/jsx-runtime"; -export default function Image(_ref) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = Image; +var _jsxRuntime = require("react/jsx-runtime"); +function Image(_ref) { let { attribs } = _ref; @@ -10,7 +16,7 @@ export default function Image(_ref) { height, width } = attribs; - return /*#__PURE__*/_jsx("img", { + return /*#__PURE__*/(0, _jsxRuntime.jsx)("img", { alt: alt, className: className, src: src, diff --git a/dist/elements/tags/selfClosing.js b/dist/elements/tags/selfClosing.js index a69312a..3ed28f1 100644 --- a/dist/elements/tags/selfClosing.js +++ b/dist/elements/tags/selfClosing.js @@ -1,14 +1,21 @@ -import attribsProps from '../../utils/attribsProps.js'; -import { jsx as _jsx } from "react/jsx-runtime"; -export default function SelfClosing(_ref) { +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = SelfClosing; +var _attribsProps = _interopRequireDefault(require("../../utils/attribsProps.js")); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } +function SelfClosing(_ref) { let { attribs, tag } = _ref; // eslint-disable-next-line no-unused-vars const Component = tag; - const attributes = attribsProps(attribs); - return /*#__PURE__*/_jsx(Component, { + const attributes = (0, _attribsProps.default)(attribs); + return /*#__PURE__*/(0, _jsxRuntime.jsx)(Component, { ...attributes }); } \ No newline at end of file diff --git a/dist/index.js b/dist/index.js index b4bd59d..28f1c1c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,7 +1,61 @@ -export { default as Block } from './components/Block.js'; -export { default as Tree } from './components/Tree.js'; -export { Provider } from './components/Context.js'; -export { customBlocks, customTags } from './elements/index.js'; -export { default as attribsProps } from './utils/attribsProps.js'; -export { default as innerNode } from './utils/innerNode.js'; -export { default as parseBlocks } from './utils/parseBlocks.js'; \ No newline at end of file +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +Object.defineProperty(exports, "Block", { + enumerable: true, + get: function () { + return _Block.default; + } +}); +Object.defineProperty(exports, "Provider", { + enumerable: true, + get: function () { + return _Context.Provider; + } +}); +Object.defineProperty(exports, "Tree", { + enumerable: true, + get: function () { + return _Tree.default; + } +}); +Object.defineProperty(exports, "attribsProps", { + enumerable: true, + get: function () { + return _attribsProps.default; + } +}); +Object.defineProperty(exports, "customBlocks", { + enumerable: true, + get: function () { + return _index.customBlocks; + } +}); +Object.defineProperty(exports, "customTags", { + enumerable: true, + get: function () { + return _index.customTags; + } +}); +Object.defineProperty(exports, "innerNode", { + enumerable: true, + get: function () { + return _innerNode.default; + } +}); +Object.defineProperty(exports, "parseBlocks", { + enumerable: true, + get: function () { + return _parseBlocks.default; + } +}); +var _Block = _interopRequireDefault(require("./components/Block")); +var _Tree = _interopRequireDefault(require("./components/Tree")); +var _Context = require("./components/Context"); +var _index = require("./elements/index"); +var _attribsProps = _interopRequireDefault(require("./utils/attribsProps")); +var _innerNode = _interopRequireDefault(require("./utils/innerNode")); +var _parseBlocks = _interopRequireDefault(require("./utils/parseBlocks")); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } \ No newline at end of file diff --git a/dist/utils/attribsProps.js b/dist/utils/attribsProps.js index 7967c90..38cbf12 100644 --- a/dist/utils/attribsProps.js +++ b/dist/utils/attribsProps.js @@ -1,15 +1,22 @@ -import convert from 'react-attr-converter'; -import parseStyle from 'style-to-js'; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _reactAttrConverter = _interopRequireDefault(require("react-attr-converter")); +var _styleToJs = _interopRequireDefault(require("style-to-js")); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const attribsProps = attribs => { if (attribs === undefined) { return {}; } const props = Object.fromEntries(Object.entries(attribs).map(attribute => { if (attribute[0] === 'style') { - return [convert(attribute[0]), parseStyle(attribute[1])]; + return [(0, _reactAttrConverter.default)(attribute[0]), (0, _styleToJs.default)(attribute[1])]; } - return [convert(attribute[0]), attribute[1]]; + return [(0, _reactAttrConverter.default)(attribute[0]), attribute[1]]; })); return props; }; -export default attribsProps; \ No newline at end of file +var _default = exports.default = attribsProps; \ No newline at end of file diff --git a/dist/utils/innerNode.js b/dist/utils/innerNode.js index 0cf2465..5f8d120 100644 --- a/dist/utils/innerNode.js +++ b/dist/utils/innerNode.js @@ -1,13 +1,19 @@ -import { parseDocument } from "htmlparser2"; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _htmlparser = require("htmlparser2"); const innerNode = (innerBlocks, innerContent) => { // If no inner blocks, return the block markup. // If inner blocks, return the wrapping markup. - const innerHtml = !innerBlocks.length ? innerContent[0] : `${innerContent[0]}[innerBlocks]${innerContent[innerContent.length - 1]}`; + const innerHtml = !innerBlocks.length ? innerContent[0] : `${innerContent[0] ?? ""}[innerBlocks]${innerContent[innerContent.length - 1] ?? ""}`; const html = innerHtml ? innerHtml.trim() : ""; - const tree = parseDocument(html, { + const tree = (0, _htmlparser.parseDocument)(html, { lowerCaseTags: true, recognizeSelfClosing: true }); return tree.children[0] ?? null; }; -export default innerNode; \ No newline at end of file +var _default = exports.default = innerNode; \ No newline at end of file diff --git a/dist/utils/parseBlocks.js b/dist/utils/parseBlocks.js index 3247c33..37e7704 100644 --- a/dist/utils/parseBlocks.js +++ b/dist/utils/parseBlocks.js @@ -1,15 +1,21 @@ -import { parse } from "@wordpress/block-serialization-default-parser"; +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = void 0; +var _blockSerializationDefaultParser = require("@wordpress/block-serialization-default-parser"); +var _Block = _interopRequireDefault(require("../components/Block.js")); +var _jsxRuntime = require("react/jsx-runtime"); +function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } // eslint-disable-next-line no-unused-vars -import Block from "../components/Block.js"; /** * Parse Gutenberg blocks from HTML markup. * * @param {string} html - markup rendered by Gutenberg editor. * @returns {JSX.Element[]} - */ -import { jsx as _jsx } from "react/jsx-runtime"; -const parseBlocks = html => parse(html.trim()).map((block, key) => /*#__PURE__*/_jsx(Block, { + */const parseBlocks = html => (0, _blockSerializationDefaultParser.parse)(html.trim()).map((block, key) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_Block.default, { block: block }, key)); -export default parseBlocks; \ No newline at end of file +var _default = exports.default = parseBlocks; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d1faf2e..cfbcf04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@frontkom/block-react-parser", - "version": "1.3.0", + "version": "1.4.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@frontkom/block-react-parser", - "version": "1.3.0", + "version": "1.4.3", "license": "ISC", "dependencies": { "@wordpress/block-serialization-default-parser": "^5.38.0", diff --git a/package.json b/package.json index 5be2aee..fe66186 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,7 @@ { "name": "@frontkom/block-react-parser", - "version": "1.4.0", + "version": "1.4.3", "description": "Gutenberg-generated HTML to React parser.", - "type": "module", "main": "dist/index.js", "module": "dist/index.js", "scripts": { @@ -47,10 +46,7 @@ "babel": { "presets": [ [ - "@babel/preset-env", - { - "modules": false - } + "@babel/preset-env" ], [ "@babel/preset-react", diff --git a/src/index.js b/src/index.js index 8579e8c..d651c03 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ -export { default as Block } from './components/Block.js'; -export { default as Tree } from './components/Tree.js'; -export { Provider } from './components/Context.js'; -export { customBlocks, customTags } from './elements/index.js'; -export { default as attribsProps } from './utils/attribsProps.js'; -export { default as innerNode } from './utils/innerNode.js'; -export { default as parseBlocks } from './utils/parseBlocks.js'; +export { default as Block } from './components/Block'; +export { default as Tree } from './components/Tree'; +export { Provider } from './components/Context'; +export { customBlocks, customTags } from './elements/index'; +export { default as attribsProps } from './utils/attribsProps'; +export { default as innerNode } from './utils/innerNode'; +export { default as parseBlocks } from './utils/parseBlocks'; diff --git a/src/utils/innerNode.jsx b/src/utils/innerNode.jsx index b380b2f..8bf8701 100644 --- a/src/utils/innerNode.jsx +++ b/src/utils/innerNode.jsx @@ -5,7 +5,9 @@ const innerNode = (innerBlocks, innerContent) => { // If inner blocks, return the wrapping markup. const innerHtml = !innerBlocks.length ? innerContent[0] - : `${innerContent[0]}[innerBlocks]${innerContent[innerContent.length - 1]}`; + : `${innerContent[0] ?? ""}[innerBlocks]${ + innerContent[innerContent.length - 1] ?? "" + }`; const html = innerHtml ? innerHtml.trim() : ""; const tree = parseDocument(html, { diff --git a/test/block.test.mjs b/test/block.test.mjs index 241473e..798a8cc 100644 --- a/test/block.test.mjs +++ b/test/block.test.mjs @@ -570,6 +570,34 @@ test('Block component renders media-text block', () => { assert.ok(rendered.includes('Text beside media.'), 'Should render text content'); }); +// ============ Null Wrapper Markup Tests ============ +test('Block component renders inner blocks when wrapper markup is null', () => { + // A block that only contains inner blocks with no surrounding HTML markup + // serializes to innerContent === [null, null]. The wrapping markup must not + // leak the string "null" into the output. + const block = { + blockName: 'core/group', + attrs: {}, + innerBlocks: [ + { + blockName: 'core/paragraph', + attrs: {}, + innerBlocks: [], + innerHTML: '

Inner content

', + innerContent: ['

Inner content

'], + }, + ], + innerHTML: '', + innerContent: [null, null], + }; + + const rendered = renderBlock(block); + + assert.ok(rendered.includes('Inner content'), 'Should render inner block content'); + assert.ok(!rendered.includes('null'), 'Should not leak literal "null" text'); + assert.ok(!rendered.includes('[innerBlocks]'), 'Should not leak the [innerBlocks] marker'); +}); + // ============ Custom Block Handler Tests ============ test('Block component uses custom handler for paragraph', () => { const html = `