Skip to content

Commit b53c961

Browse files
committed
fix: configure Babel to output ESM and add file extensions
- Update Babel preset-env to not transpile modules (modules: false) - Add .js file extensions to all import paths for ESM compatibility - Update build test script to run 'npm run build' before 'node --test' This allows the package to work properly as an ES module with explicit file extensions required by Node.js ESM resolution. All 30 block rendering tests now pass.
1 parent 5e0350b commit b53c961

18 files changed

Lines changed: 393 additions & 20 deletions

File tree

‎dist/components/Block.js‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import Tree from "./Tree.js";
3+
import innerNode from "../utils/innerNode.js";
4+
import { useBlockComponent } from "./Context.js";
5+
6+
/**
7+
* Block element.
8+
*
9+
* @param {object} componentProps - properties that includes the block object.
10+
* @returns {JSX.Element | null | undefined}
11+
*/
12+
import { jsx as _jsx } from "react/jsx-runtime";
13+
export default function Block(_ref) {
14+
let {
15+
block
16+
} = _ref;
17+
const {
18+
blockName = null,
19+
innerContent,
20+
innerBlocks
21+
} = block;
22+
const CustomBlock = useBlockComponent(blockName);
23+
if (CustomBlock) {
24+
return /*#__PURE__*/_jsx(CustomBlock, {
25+
block: block
26+
});
27+
}
28+
29+
// Filter out empty blocks.
30+
if (!blockName && innerContent.length === 0) {
31+
return null;
32+
}
33+
// Filter out empty lines and orphaned closing tags.
34+
if (innerContent.length === 1 && (innerContent[0] === "\n" || innerContent[0].substring(0, 2) === "</")) {
35+
return null;
36+
}
37+
const node = innerNode(innerBlocks, innerContent);
38+
if (node) {
39+
return /*#__PURE__*/_jsx(Tree, {
40+
node: node,
41+
block: block
42+
});
43+
}
44+
return null;
45+
}

‎dist/components/Context.js‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { createContext, useContext } from 'react';
2+
import { coreTags, coreBlocks } from '../elements/index.js';
3+
const TxContext = /*#__PURE__*/createContext();
4+
export const {
5+
Provider
6+
} = TxContext;
7+
export function useComponentContext() {
8+
return useContext(TxContext);
9+
}
10+
export function useBlockComponent(name) {
11+
const {
12+
CustomBlocks = coreBlocks
13+
} = useContext(TxContext);
14+
return name && CustomBlocks[name];
15+
}
16+
export function useTagComponent(tag) {
17+
const {
18+
CustomTags = coreTags
19+
} = useContext(TxContext);
20+
return tag && CustomTags[tag];
21+
}

‎dist/components/Tree.js‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { useTagComponent } from "./Context.js";
2+
// eslint-disable-next-line no-unused-vars
3+
import Block from "./Block.js";
4+
import attribsProps from "../utils/attribsProps.js";
5+
import { jsx as _jsx } from "react/jsx-runtime";
6+
export default function Tree(_ref) {
7+
let {
8+
node,
9+
block
10+
} = _ref;
11+
const CustomTag = useTagComponent(node.name);
12+
attribsProps(node.attribs);
13+
if (node.type === "text") {
14+
if (node.data.trim() === "[innerBlocks]") {
15+
return block.innerBlocks?.map((inner, index) => /*#__PURE__*/_jsx(Block, {
16+
block: inner
17+
}, index));
18+
}
19+
return node.data;
20+
}
21+
if (CustomTag) {
22+
const children = node.children?.map((child, index) => /*#__PURE__*/_jsx(Tree, {
23+
node: child,
24+
block: block
25+
}, index));
26+
return /*#__PURE__*/_jsx(CustomTag, {
27+
attribs: node.attribs,
28+
node: children,
29+
block: block
30+
});
31+
}
32+
33+
// Component is used as a dynamic tag name in JSX
34+
// eslint-disable-next-line no-unused-vars
35+
const Component = node.name;
36+
const attrs = attribsProps(node.attribs);
37+
return /*#__PURE__*/_jsx(Component, {
38+
...attrs,
39+
children: node.children?.map((child, index) => /*#__PURE__*/_jsx(Tree, {
40+
node: child,
41+
block: block
42+
}, index))
43+
});
44+
}

‎dist/elements/index.js‎

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
// eslint-disable-next-line no-unused-vars
2+
import Image from './tags/img.js';
3+
// eslint-disable-next-line no-unused-vars
4+
import SelfClosing from './tags/selfClosing.js';
5+
import { jsx as _jsx } from "react/jsx-runtime";
6+
export const coreTags = {
7+
img: _ref => {
8+
let {
9+
attribs
10+
} = _ref;
11+
return /*#__PURE__*/_jsx(Image, {
12+
attribs: attribs
13+
});
14+
},
15+
br: _ref2 => {
16+
let {
17+
attribs
18+
} = _ref2;
19+
return /*#__PURE__*/_jsx(SelfClosing, {
20+
attribs: attribs,
21+
tag: "br"
22+
});
23+
},
24+
hr: _ref3 => {
25+
let {
26+
attribs
27+
} = _ref3;
28+
return /*#__PURE__*/_jsx(SelfClosing, {
29+
attribs: attribs,
30+
tag: "hr"
31+
});
32+
},
33+
meta: _ref4 => {
34+
let {
35+
_attribs
36+
} = _ref4;
37+
return null;
38+
}
39+
// area: ({attribs}) => <SelfClosing attribs={attribs} tag="area" />,
40+
// base: ({attribs}) => <SelfClosing attribs={attribs} tag="base" />,
41+
// col: ({attribs}) => <SelfClosing attribs={attribs} tag="col" />,
42+
// embed: ({attribs}) => <SelfClosing attribs={attribs} tag="embed" />,
43+
// input: ({attribs}) => <SelfClosing attribs={attribs} tag="input" />,
44+
// link: ({attribs}) => <SelfClosing attribs={attribs} tag="link" />,
45+
// meta: ({attribs}) => <SelfClosing attribs={attribs} tag="meta" />,
46+
// param: ({attribs}) => <SelfClosing attribs={attribs} tag="param" />,
47+
// source: ({attribs}) => <SelfClosing attribs={attribs} tag="source" />,
48+
// track: ({attribs}) => <SelfClosing attribs={attribs} tag="track" />,
49+
// wbr: ({attribs}) => <SelfClosing attribs={attribs} tag="wbr" />,
50+
};
51+
export const coreBlocks = {
52+
// 'core/archives': ({ block }) => doSomething(),
53+
// 'core/audio': ({ block }) => doSomething(),
54+
// 'core/avatar': ({ block }) => doSomething(),
55+
// 'core/block': ({ block }) => doSomething(),
56+
// 'core/button': ({ block }) => doSomething(),
57+
// 'core/buttons': ({ block }) => doSomething(),
58+
// 'core/calendar': ({ block }) => doSomething(),
59+
// 'core/categories': ({ block }) => doSomething(),
60+
// 'core/code': ({ block }) => doSomething(),
61+
// 'core/column': ({ block }) => <p>Column</p>,
62+
// 'core/columns': ({ block }) => doSomething(),
63+
// 'core/comment-author-avatar': ({ block }) => doSomething(),
64+
// 'core/comment-author-name': ({ block }) => doSomething(),
65+
// 'core/comment-content': ({ block }) => doSomething(),
66+
// 'core/comment-date': ({ block }) => doSomething(),
67+
// 'core/comment-edit-link': ({ block }) => doSomething(),
68+
// 'core/comment-reply-link': ({ block }) => doSomething(),
69+
// 'core/comment-template': ({ block }) => doSomething(),
70+
// 'core/comments': ({ block }) => doSomething(),
71+
// 'core/comments-pagination': ({ block }) => doSomething(),
72+
// 'core/comments-pagination-next': ({ block }) => doSomething(),
73+
// 'core/comments-pagination-numbers': ({ block }) => doSomething(),
74+
// 'core/comments-pagination-previous': ({ block }) => doSomething(),
75+
// 'core/comments-title': ({ block }) => doSomething(),
76+
// 'core/cover': ({ block }) => doSomething(),
77+
// 'core/embed': ({ block }) => doSomething(),
78+
// 'core/file': ({ block }) => doSomething(),
79+
// 'core/freeform': ({ block }) => doSomething(),
80+
// 'core/gallery': ({ block }) => doSomething(),
81+
// 'core/group': ({ block }) => doSomething(),
82+
// 'core/heading': ({ block }) => doSomething(),
83+
// 'core/home-link': ({ block }) => doSomething(),
84+
// 'core/html': ({ block }) => doSomething(),
85+
// 'core/image': ({ block }) => doSomething(),
86+
// 'core/latest-comments': ({ block }) => doSomething(),
87+
// 'core/latest-posts': ({ block }) => doSomething(),
88+
// 'core/list': ({ block }) => doSomething(),
89+
// 'core/list-item': ({ block }) => doSomething(),
90+
// 'core/loginout': ({ block }) => doSomething(),
91+
// 'core/media-text': ({ block }) => doSomething(),
92+
// 'core/missing': ({ block }) => doSomething(),
93+
// 'core/more': ({ block }) => doSomething(),
94+
// 'core/navigation': ({ block }) => doSomething(),
95+
// 'core/navigation-link': ({ block }) => doSomething(),
96+
// 'core/navigation-submenu': ({ block }) => doSomething(),
97+
// 'core/nextpage': ({ block }) => doSomething(),
98+
// 'core/page-list': ({ block }) => doSomething(),
99+
// 'core/paragraph': ({ block }) => doSomething(),
100+
// 'core/pattern': ({ block }) => doSomething(),
101+
// 'core/post-author': ({ block }) => doSomething(),
102+
// 'core/post-author-biography': ({ block }) => doSomething(),
103+
// 'core/post-author-name': ({ block }) => doSomething(),
104+
// 'core/post-comment': ({ block }) => doSomething(),
105+
// 'core/post-comments-count': ({ block }) => doSomething(),
106+
// 'core/post-comments-form': ({ block }) => doSomething(),
107+
// 'core/post-comments-link': ({ block }) => doSomething(),
108+
// 'core/post-content': ({ block }) => doSomething(),
109+
// 'core/post-date': ({ block }) => doSomething(),
110+
// 'core/post-excerpt': ({ block }) => doSomething(),
111+
// 'core/post-featured-image': ({ block }) => doSomething(),
112+
// 'core/post-navigation-link': ({ block }) => doSomething(),
113+
// 'core/post-template': ({ block }) => doSomething(),
114+
// 'core/post-terms': ({ block }) => doSomething(),
115+
// 'core/post-title': ({ block }) => doSomething(),
116+
// 'core/preformatted': ({ block }) => doSomething(),
117+
// 'core/pullquote': ({ block }) => doSomething(),
118+
// 'core/query': ({ block }) => doSomething(),
119+
// 'core/query-no-results': ({ block }) => doSomething(),
120+
// 'core/query-pagination': ({ block }) => doSomething(),
121+
// 'core/query-pagination-next': ({ block }) => doSomething(),
122+
// 'core/query-pagination-numbers': ({ block }) => doSomething(),
123+
// 'core/query-pagination-previous': ({ block }) => doSomething(),
124+
// 'core/query-title': ({ block }) => doSomething(),
125+
// 'core/quote': ({ block }) => doSomething(),
126+
// 'core/read-more': ({ block }) => doSomething(),
127+
// 'core/rss': ({ block }) => doSomething(),
128+
// 'core/search': ({ block }) => doSomething(),
129+
// 'core/separator': ({ block }) => doSomething(),
130+
// 'core/shortcode': ({ block }) => doSomething(),
131+
// 'core/site-logo': ({ block }) => doSomething(),
132+
// 'core/site-tagline': ({ block }) => doSomething(),
133+
// 'core/site-title': ({ block }) => doSomething(),
134+
// 'core/social-link': ({ block }) => doSomething(),
135+
// 'core/social-links': ({ block }) => doSomething(),
136+
// 'core/spacer': ({ block }) => doSomething(),
137+
// 'core/table': ({ block }) => doSomething(),
138+
// 'core/table-of-contents': ({ block }) => doSomething(),
139+
// 'core/tag-cloud': ({ block }) => doSomething(),
140+
// 'core/template-part': ({ block }) => doSomething(),
141+
// 'core/term-description': ({ block }) => doSomething(),
142+
// 'core/text-columns': ({ block }) => doSomething(),
143+
// 'core/verse': ({ block }) => doSomething(),
144+
// 'core/video': ({ block }) => doSomething(),
145+
};
146+
147+
/**
148+
* Helper function to setup custom tags definitions.
149+
*
150+
* @param {object} tags - Optional object with custom blocks definitions. Empty by default.
151+
* @returns {object} Object with blocks definitions
152+
*/
153+
export const customTags = function () {
154+
let tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
155+
return {
156+
...coreTags,
157+
...tags
158+
};
159+
};
160+
161+
/**
162+
* Helper function to setup custom blocks definitions.
163+
*
164+
* @param {object} blocks - Optional object with custom blocks definitions. Empty by default.
165+
* @param {boolean} useDefaultBlocks - Optional boolean to use core blocks defaults. True by default.
166+
* @returns {object} Object with blocks definitions
167+
*/
168+
export const customBlocks = function () {
169+
let blocks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
170+
return {
171+
...coreBlocks,
172+
...blocks
173+
};
174+
};

‎dist/elements/tags/img.js‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { jsx as _jsx } from "react/jsx-runtime";
2+
export default function Image(_ref) {
3+
let {
4+
attribs
5+
} = _ref;
6+
const {
7+
alt = '',
8+
class: className = '',
9+
src,
10+
height,
11+
width
12+
} = attribs;
13+
return /*#__PURE__*/_jsx("img", {
14+
alt: alt,
15+
className: className,
16+
src: src,
17+
width: width,
18+
height: height
19+
});
20+
}

‎dist/elements/tags/selfClosing.js‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import attribsProps from '../../utils/attribsProps.js';
2+
import { jsx as _jsx } from "react/jsx-runtime";
3+
export default function SelfClosing(_ref) {
4+
let {
5+
attribs,
6+
tag
7+
} = _ref;
8+
// eslint-disable-next-line no-unused-vars
9+
const Component = tag;
10+
const attributes = attribsProps(attribs);
11+
return /*#__PURE__*/_jsx(Component, {
12+
...attributes
13+
});
14+
}

‎dist/index.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export { default as Block } from './components/Block.js';
2+
export { default as Tree } from './components/Tree.js';
3+
export { Provider } from './components/Context.js';
4+
export { customBlocks, customTags } from './elements/index.js';
5+
export { default as attribsProps } from './utils/attribsProps.js';
6+
export { default as innerNode } from './utils/innerNode.js';
7+
export { default as parseBlocks } from './utils/parseBlocks.js';

‎dist/utils/attribsProps.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import convert from 'react-attr-converter';
2+
import parseStyle from 'style-to-js';
3+
const attribsProps = attribs => {
4+
if (attribs === undefined) {
5+
return {};
6+
}
7+
const props = Object.fromEntries(Object.entries(attribs).map(attribute => {
8+
if (attribute[0] === 'style') {
9+
return [convert(attribute[0]), parseStyle(attribute[1])];
10+
}
11+
return [convert(attribute[0]), attribute[1]];
12+
}));
13+
return props;
14+
};
15+
export default attribsProps;

‎dist/utils/innerNode.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { parseDocument } from "htmlparser2";
2+
const innerNode = (innerBlocks, innerContent) => {
3+
// If no inner blocks, return the block markup.
4+
// If inner blocks, return the wrapping markup.
5+
const innerHtml = !innerBlocks.length ? innerContent[0] : `${innerContent[0]}[innerBlocks]${innerContent[innerContent.length - 1]}`;
6+
const html = innerHtml ? innerHtml.trim() : "";
7+
const tree = parseDocument(html, {
8+
lowerCaseTags: true,
9+
recognizeSelfClosing: true
10+
});
11+
return tree.children[0] ?? null;
12+
};
13+
export default innerNode;

‎dist/utils/parseBlocks.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { parse } from "@wordpress/block-serialization-default-parser";
2+
// eslint-disable-next-line no-unused-vars
3+
import Block from "../components/Block.js";
4+
5+
/**
6+
* Parse Gutenberg blocks from HTML markup.
7+
*
8+
* @param {string} html - markup rendered by Gutenberg editor.
9+
* @returns {JSX.Element[]}
10+
*/
11+
import { jsx as _jsx } from "react/jsx-runtime";
12+
const parseBlocks = html => parse(html.trim()).map((block, key) => /*#__PURE__*/_jsx(Block, {
13+
block: block
14+
}, key));
15+
export default parseBlocks;

0 commit comments

Comments
 (0)