@adobe/helix-md2jcr 1.4.1 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.5.0](https://github.com/adobe/helix-md2jcr/compare/v1.4.1...v1.5.0) (2026-06-17)
2
+
3
+
4
+ ### Features
5
+
6
+ * **phrasing:** preserve inline HTML tags in text elements ([#217](https://github.com/adobe/helix-md2jcr/issues/217)) ([5f7d274](https://github.com/adobe/helix-md2jcr/commit/5f7d2745005ae0947866fb66989cdb9626cb3e60))
7
+
1
8
  ## [1.4.1](https://github.com/adobe/helix-md2jcr/compare/v1.4.0...v1.4.1) (2026-06-09)
2
9
 
3
10
 
package/README.md CHANGED
@@ -114,6 +114,8 @@ console.log(xml);
114
114
  cell is routed back to fields, mirroring block options.
115
115
  - [Field Hinting](docs/field-hinting.md) — using `<!-- field: name -->` to skip
116
116
  ahead within a field group.
117
+ - [Phrasing Content](docs/phrasing.md) — supported inline HTML tags (`sup`, `sub`,
118
+ `u`, `del`, `strong`, `em`) and how they survive the markdown → JCR pipeline.
117
119
 
118
120
  ## Baseline XML Files
119
121
  Running the ./baseline-tests.sh script will detect any md file under test/fixtures and execute the convert2jcr node script.
@@ -0,0 +1,54 @@
1
+ # Phrasing Content (Inline HTML)
2
+
3
+ Phrasing content refers to inline elements that flow within a line of text — as
4
+ opposed to block-level elements like paragraphs, headings, and lists.
5
+
6
+ When raw HTML inline tags appear in markdown source, remark parses them as `html`
7
+ nodes. The `mdast-sanitize-html` step converts them into typed mdast nodes, and
8
+ the serializer then maps those nodes back to their HTML equivalents when writing
9
+ the `text` attribute of a JCR text component.
10
+
11
+ ## Supported Tags
12
+
13
+ | HTML tag | mdast node type | Serializes to |
14
+ |---|---|---|
15
+ | `<sup>` | `superscript` | `<sup>` |
16
+ | `<sub>` | `subscript` | `<sub>` |
17
+ | `<u>` | `underline` | `<u>` |
18
+ | `<del>` | `delete` | `<del>` |
19
+ | `<strong>` | `strong` | `<strong>` |
20
+ | `<em>` | `emphasis` | `<em>` |
21
+
22
+ `superscript`, `subscript`, and `underline` are custom node types introduced by
23
+ `mdast-sanitize-html`. They require explicit handlers in `hast-handlers.js` so
24
+ that `mdast-util-to-hast` knows to emit the correct tag rather than falling back
25
+ to `<div>`. The remaining three (`delete`, `strong`, `emphasis`) are standard
26
+ mdast types with built-in hast handlers.
27
+
28
+ ## Example
29
+
30
+ Markdown source:
31
+
32
+ ```markdown
33
+ E = mc<sup>2</sup> and H<sub>2</sub>O with <u>underline</u>, <del>strike</del>, <strong>bold</strong>, <em>italic</em>.
34
+ ```
35
+
36
+ Resulting `text` attribute value:
37
+
38
+ ```html
39
+ <p>E = mc<sup>2</sup> and H<sub>2</sub>O with <u>underline</u>, <del>strike</del>, <strong>bold</strong>, <em>italic</em>.</p>
40
+ ```
41
+
42
+ ## Pipeline
43
+
44
+ ```
45
+ markdown source
46
+ → remark parse → html nodes (raw inline HTML)
47
+ → mdast-sanitize-html → typed mdast nodes (superscript, subscript, etc.)
48
+ → toHast (with customHastHandlers) → hast element nodes
49
+ → toHtml → HTML string stored in JCR text attribute
50
+ ```
51
+
52
+ The custom handlers live in
53
+ `src/mdast2jcr/hb/partials/supports/hast-handlers.js` and are applied wherever
54
+ `toHast` is called — currently `paragraph.js` and `grid-table.js`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-md2jcr",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "Helix Service that converts markdown to JCR content structure",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -16,6 +16,7 @@ import { toString } from 'mdast-util-to-string';
16
16
  import Handlebars from 'handlebars';
17
17
  import { toHast } from 'mdast-util-to-hast';
18
18
  import { toHtml } from 'hast-util-to-html';
19
+ import { customHastHandlers } from './supports/hast-handlers.js';
19
20
  import { getComponentById, getComponentByTitle, getModelId } from '../../domain/Definitions.js';
20
21
  import {
21
22
  findModelById,
@@ -177,6 +178,7 @@ function extractPropertiesForNode(field, currentNode, properties) {
177
178
  value = currentNode.children.reduce((acc, node) => {
178
179
  const hast = toHast(node, {
179
180
  allowDangerousHtml: true,
181
+ handlers: customHastHandlers,
180
182
  });
181
183
 
182
184
  let str = toHtml(hast, {
@@ -190,7 +192,7 @@ function extractPropertiesForNode(field, currentNode, properties) {
190
192
  return acc + encodeHtml(str);
191
193
  }, '');
192
194
  } else {
193
- value = encodeHtml(toHtml(toHast(currentNode)));
195
+ value = encodeHtml(toHtml(toHast(currentNode, { handlers: customHastHandlers })));
194
196
  }
195
197
 
196
198
  // if the node is a code block then don't strip out the newlines
@@ -13,10 +13,11 @@ import Handlebars from 'handlebars';
13
13
 
14
14
  import { toHast } from 'mdast-util-to-hast';
15
15
  import { toHtml } from 'hast-util-to-html';
16
+ import { customHastHandlers } from './supports/hast-handlers.js';
16
17
 
17
18
  function paragraph(context) {
18
19
  const html = context.children.map((child) => {
19
- const hast = toHast(child);
20
+ const hast = toHast(child, { handlers: customHastHandlers });
20
21
  return toHtml(hast);
21
22
  }).join('');
22
23
 
@@ -0,0 +1,40 @@
1
+ /*
2
+ * Copyright 2026 Adobe. All rights reserved.
3
+ * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
+ * you may not use this file except in compliance with the License. You may obtain a copy
5
+ * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ *
7
+ * Unless required by applicable law or agreed to in writing, software distributed under
8
+ * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
+ * OF ANY KIND, either express or implied. See the License for the specific language
10
+ * governing permissions and limitations under the License.
11
+ */
12
+
13
+ /**
14
+ * Creates a hast handler that maps a custom mdast node type to an inline HTML element.
15
+ * mdast-util-to-hast has no built-in knowledge of superscript/subscript/underline nodes
16
+ * (introduced by mdast-sanitize-html), so without these handlers those nodes fall back
17
+ * to <div>, losing the semantic tag.
18
+ *
19
+ * @param {string} tagName - HTML tag to emit (e.g. 'sup', 'sub', 'u')
20
+ * @returns {function(State, Node): Element} hast handler
21
+ */
22
+ function formatHandler(tagName) {
23
+ return (state, node) => ({
24
+ type: 'element',
25
+ tagName,
26
+ properties: {},
27
+ children: state.all(node),
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Custom hast handlers for mdast node types that mdast-util-to-hast does not recognize
33
+ * by default. Pass these as the `handlers` option wherever toHast() is called so that
34
+ * inline formatting round-trips correctly through the mdast → hast → HTML pipeline.
35
+ */
36
+ export const customHastHandlers = {
37
+ superscript: formatHandler('sup'),
38
+ subscript: formatHandler('sub'),
39
+ underline: formatHandler('u'),
40
+ };