@adobe/helix-md2jcr 1.0.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 +15 -0
- package/CODE_OF_CONDUCT.md +74 -0
- package/CONTRIBUTING.md +74 -0
- package/LICENSE.txt +264 -0
- package/README.md +27 -0
- package/baseline-tests.sh +45 -0
- package/package.json +81 -0
- package/src/cli/convert2jcr.js +128 -0
- package/src/index.d.ts +16 -0
- package/src/index.js +15 -0
- package/src/md2jcr/index.d.ts +20 -0
- package/src/md2jcr/index.js +36 -0
- package/src/mdast2jcr/domain/Component.js +66 -0
- package/src/mdast2jcr/domain/Definitions.js +78 -0
- package/src/mdast2jcr/domain/FieldGroup.js +85 -0
- package/src/mdast2jcr/domain/FieldGroupFieldResolver.js +96 -0
- package/src/mdast2jcr/domain/ModelHelper.js +123 -0
- package/src/mdast2jcr/domain/Models.js +46 -0
- package/src/mdast2jcr/hast-table-cell-handler.js +48 -0
- package/src/mdast2jcr/hast-table-handler.js +126 -0
- package/src/mdast2jcr/hb/helpers/encode-helper.js +18 -0
- package/src/mdast2jcr/hb/helpers/name-helper.js +61 -0
- package/src/mdast2jcr/hb/helpers/page-helper.js +109 -0
- package/src/mdast2jcr/hb/helpers/section-helper.js +53 -0
- package/src/mdast2jcr/hb/helpers/which-partial-helper.js +22 -0
- package/src/mdast2jcr/hb/partials/blockquote.js +16 -0
- package/src/mdast2jcr/hb/partials/emphasis.js +34 -0
- package/src/mdast2jcr/hb/partials/grid-table.js +495 -0
- package/src/mdast2jcr/hb/partials/heading.js +53 -0
- package/src/mdast2jcr/hb/partials/image.js +24 -0
- package/src/mdast2jcr/hb/partials/link.js +33 -0
- package/src/mdast2jcr/hb/partials/paragraph.js +30 -0
- package/src/mdast2jcr/hb/partials/strong.js +33 -0
- package/src/mdast2jcr/hb/partials/supports/image.js +27 -0
- package/src/mdast2jcr/hb/partials/supports/link.js +46 -0
- package/src/mdast2jcr/hb/partials/table.js +16 -0
- package/src/mdast2jcr/index.d.ts +122 -0
- package/src/mdast2jcr/index.js +94 -0
- package/src/mdast2jcr/mdast-download-images.js +165 -0
- package/src/mdast2jcr/mdast-sanitize-html.js +233 -0
- package/src/mdast2jcr/templates/page-template.js +27 -0
- package/src/mdast2jcr/utils/mdast.js +47 -0
- package/src/mdast2jcr/utils.js +233 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 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
|
+
import { visit } from 'unist-util-visit';
|
|
13
|
+
import { unified } from 'unified';
|
|
14
|
+
import parse from 'rehype-parse';
|
|
15
|
+
import { defaultHandlers, toMdast } from 'hast-util-to-mdast';
|
|
16
|
+
import tableHandler from './hast-table-handler.js';
|
|
17
|
+
import tableCellHandler from './hast-table-cell-handler.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates simple format handler
|
|
21
|
+
* @param type
|
|
22
|
+
*/
|
|
23
|
+
function formatHandler(type) {
|
|
24
|
+
return (state, node) => {
|
|
25
|
+
const result = { type, children: state.all(node) };
|
|
26
|
+
state.patch(node, result);
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {State} state
|
|
33
|
+
* State.
|
|
34
|
+
* @param {Readonly<Element>} node
|
|
35
|
+
* hast element to transform.
|
|
36
|
+
* @returns {Link}
|
|
37
|
+
* mdast node.
|
|
38
|
+
*/
|
|
39
|
+
export function linkHandler(state, node) {
|
|
40
|
+
const properties = node.properties || {};
|
|
41
|
+
// Allow potentially “invalid” nodes, they might be unknown.
|
|
42
|
+
// We also support straddling later.
|
|
43
|
+
const children = /** @type {Array<PhrasingContent>} */ (state.all(node));
|
|
44
|
+
|
|
45
|
+
/** @type {Link} */
|
|
46
|
+
const result = {
|
|
47
|
+
type: 'link',
|
|
48
|
+
url: state.resolve(String(properties.href || '') || null),
|
|
49
|
+
title: properties.title ? String(properties.title) : null,
|
|
50
|
+
anchor: properties.name ?? properties.id,
|
|
51
|
+
|
|
52
|
+
// if the link has a child of type link, then we need to unwrap it
|
|
53
|
+
children: children.length === 1 && children[0].type === 'link' ? children[0].children : children,
|
|
54
|
+
};
|
|
55
|
+
state.patch(node, result);
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* removes paragraphs from the child nodes recursively.
|
|
61
|
+
* @param node
|
|
62
|
+
*/
|
|
63
|
+
function unwrapParagraphs(node) {
|
|
64
|
+
if (!node.children) {
|
|
65
|
+
return node;
|
|
66
|
+
}
|
|
67
|
+
for (let idx = 0; idx < node.children.length; idx += 1) {
|
|
68
|
+
const child = node.children[idx];
|
|
69
|
+
if (child.type === 'paragraph') {
|
|
70
|
+
node.children.splice(idx, 1, ...child.children);
|
|
71
|
+
idx += child.children.length - 1;
|
|
72
|
+
} else {
|
|
73
|
+
// eslint-disable-next-line no-param-reassign
|
|
74
|
+
node.children[idx] = unwrapParagraphs(child);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return node;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Handler for `<markdown>` elements.
|
|
82
|
+
* @param {[]} mdasts array of mdast sub trees
|
|
83
|
+
*/
|
|
84
|
+
function mdHandler(mdasts) {
|
|
85
|
+
return (state, node) => {
|
|
86
|
+
// given the index property from the <markdown idx> element, return the
|
|
87
|
+
// corresponding mdast sub tree from the mdasts array.
|
|
88
|
+
const { idx } = node.properties;
|
|
89
|
+
return mdasts[+idx];
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isPhrasingParent(node) {
|
|
94
|
+
return [
|
|
95
|
+
'paragraph',
|
|
96
|
+
'underline',
|
|
97
|
+
'subscript',
|
|
98
|
+
'superscript',
|
|
99
|
+
'heading',
|
|
100
|
+
'emphasis',
|
|
101
|
+
'strong',
|
|
102
|
+
'link',
|
|
103
|
+
'linkReference',
|
|
104
|
+
'tableCell',
|
|
105
|
+
'delete',
|
|
106
|
+
'footnote',
|
|
107
|
+
].includes(node.type);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Find the index of the last HTML node in the list of siblings, if no HTML node
|
|
112
|
+
* is found, return an index of 0;
|
|
113
|
+
* @param siblings list of siblings to search through to find the last HTML node
|
|
114
|
+
* @returns {number}
|
|
115
|
+
*/
|
|
116
|
+
export function locateIndexOfLastHtmlNode(siblings) {
|
|
117
|
+
const index = [...siblings]
|
|
118
|
+
.reverse()
|
|
119
|
+
.findIndex((sibling) => sibling.type === 'html');
|
|
120
|
+
|
|
121
|
+
// restore the order of the siblings array before returning the index
|
|
122
|
+
return index === -1 ? 0 : siblings.length - index - 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Sanitizes html:
|
|
127
|
+
* - collapses consecutive html content (simply concat all nodes until the last html sibling)
|
|
128
|
+
* - parses and converts them to mdast again
|
|
129
|
+
*
|
|
130
|
+
* @param {object} tree
|
|
131
|
+
* @returns {object} The modified (original) tree.
|
|
132
|
+
*/
|
|
133
|
+
export default function sanitizeHtml(tree) {
|
|
134
|
+
const mdInserts = [];
|
|
135
|
+
|
|
136
|
+
visit(tree, (node, index, parent) => {
|
|
137
|
+
const { children: siblings = [] } = parent || {};
|
|
138
|
+
|
|
139
|
+
// collapse html blocks
|
|
140
|
+
if (node.type === 'html') {
|
|
141
|
+
// find the index of the last html node in the list of siblings
|
|
142
|
+
const lastHtmlIndex = locateIndexOfLastHtmlNode(siblings, index);
|
|
143
|
+
|
|
144
|
+
// get the node's value and store it in html variable
|
|
145
|
+
let html = node.value;
|
|
146
|
+
|
|
147
|
+
// if there are more html nodes after this one, remove them from the siblings array
|
|
148
|
+
if (lastHtmlIndex > index) {
|
|
149
|
+
// splice the siblings array to remove the nodes that are between the
|
|
150
|
+
// current node and the last html node, leaving the siblings array with
|
|
151
|
+
// only the nodes that come before the first HTML node, and include the
|
|
152
|
+
// first HTML node
|
|
153
|
+
const removed = siblings.splice(index + 1, lastHtmlIndex - index);
|
|
154
|
+
|
|
155
|
+
// append to html as special markdown element marker which is then handled in the
|
|
156
|
+
// mdHandler for the `<markdown>` elements.
|
|
157
|
+
removed.forEach((n) => {
|
|
158
|
+
if (n.type === 'html' || n.type === 'text') {
|
|
159
|
+
html += n.value;
|
|
160
|
+
} else {
|
|
161
|
+
// eventually toMdast will be called, and when we encounter a markdown element
|
|
162
|
+
// we need to replace it with the actual markdown content, so store the index
|
|
163
|
+
// such that we can look it up in the mdInserts array later.
|
|
164
|
+
html += `<markdown idx="${mdInserts.length}"></markdown>`;
|
|
165
|
+
mdInserts.push(n);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (isPhrasingParent(parent)) {
|
|
171
|
+
html = `<p>${html}</p>`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// try parse html
|
|
175
|
+
const hast = unified()
|
|
176
|
+
.use(parse, { fragment: true })
|
|
177
|
+
.parse(html);
|
|
178
|
+
|
|
179
|
+
// convert to mdast with extra handlers
|
|
180
|
+
const mdast = toMdast(hast, {
|
|
181
|
+
document: false,
|
|
182
|
+
handlers: {
|
|
183
|
+
...defaultHandlers,
|
|
184
|
+
a: linkHandler,
|
|
185
|
+
u: formatHandler('underline'),
|
|
186
|
+
sub: formatHandler('subscript'),
|
|
187
|
+
sup: formatHandler('superscript'),
|
|
188
|
+
table: tableHandler,
|
|
189
|
+
markdown: mdHandler(mdInserts),
|
|
190
|
+
th: tableCellHandler,
|
|
191
|
+
td: tableCellHandler,
|
|
192
|
+
},
|
|
193
|
+
});
|
|
194
|
+
// clear inserts
|
|
195
|
+
mdInserts.length = 0;
|
|
196
|
+
|
|
197
|
+
// ensure that flow nodes are in phrasing context
|
|
198
|
+
if (!isPhrasingParent(parent)) {
|
|
199
|
+
let lastParagraph;
|
|
200
|
+
for (let idx = 0; idx < mdast.children.length; idx += 1) {
|
|
201
|
+
const child = mdast.children[idx];
|
|
202
|
+
if (child.type === 'underline' || child.type === 'subscript' || child.type === 'superscript') {
|
|
203
|
+
unwrapParagraphs(child);
|
|
204
|
+
if (!lastParagraph) {
|
|
205
|
+
lastParagraph = {
|
|
206
|
+
type: 'paragraph',
|
|
207
|
+
children: [child],
|
|
208
|
+
};
|
|
209
|
+
mdast.children.splice(idx, 1, lastParagraph);
|
|
210
|
+
} else {
|
|
211
|
+
lastParagraph.children.push(child);
|
|
212
|
+
mdast.children.splice(idx, 1);
|
|
213
|
+
idx -= 1;
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
lastParagraph = null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} else {
|
|
220
|
+
unwrapParagraphs(mdast);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// inject children of parsed tree
|
|
224
|
+
siblings.splice(index, 1, ...mdast.children);
|
|
225
|
+
|
|
226
|
+
// continue after
|
|
227
|
+
return index + mdast.children.length;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return visit.CONTINUE;
|
|
231
|
+
});
|
|
232
|
+
return tree;
|
|
233
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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
|
+
export default function pageTemplate() {
|
|
13
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
14
|
+
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0" jcr:primaryType="cq:Page">
|
|
15
|
+
{{#page}}
|
|
16
|
+
<root jcr:primaryType="nt:unstructured" sling:resourceType="core/franklin/components/root/v1/root">
|
|
17
|
+
{{#each children }}
|
|
18
|
+
{{#section @index children }}
|
|
19
|
+
{{#each children }}
|
|
20
|
+
{{> (whichPartial this.type) models=../../models definition=../../definition filters=../../filters}}
|
|
21
|
+
{{/each}}
|
|
22
|
+
{{/section}}
|
|
23
|
+
{{/each}}
|
|
24
|
+
</root>
|
|
25
|
+
{{/page}}
|
|
26
|
+
</jcr:root>`;
|
|
27
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 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
|
+
* findAll goes through the children of a node searching for nodes that match a
|
|
15
|
+
* test function.
|
|
16
|
+
*
|
|
17
|
+
* @param node the node to search through.
|
|
18
|
+
* @param test the test function
|
|
19
|
+
* @param flatten if true, the function will return the children of the matching nodes, without
|
|
20
|
+
* their parent.
|
|
21
|
+
* @return {*[]} an array of nodes that match the test function.
|
|
22
|
+
*/
|
|
23
|
+
function findAll(node, test, flatten = false) {
|
|
24
|
+
// given the root of the mdast tree, find all the nodes that are identified by the test function
|
|
25
|
+
const results = [];
|
|
26
|
+
|
|
27
|
+
if (test(node)) {
|
|
28
|
+
results.push(node);
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (node.children) {
|
|
33
|
+
node.children.forEach((child) => {
|
|
34
|
+
results.push(...findAll(child, test));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (flatten) {
|
|
39
|
+
return results.map((c) => c.children).flat();
|
|
40
|
+
} else {
|
|
41
|
+
return results;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
findAll,
|
|
47
|
+
};
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 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
|
+
import Handlebars from 'handlebars';
|
|
13
|
+
import { toString } from 'mdast-util-to-string';
|
|
14
|
+
|
|
15
|
+
/* eslint-disable no-param-reassign */
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This method modifies the mdast tree in place. Given a flat list of nodes,
|
|
19
|
+
* gather the children of each section and wrap them in a section node.
|
|
20
|
+
* For example given a mdast tree of:
|
|
21
|
+
* [Heading, Text, Image, ThematicBreak, Text, ThematicBreak, Text] would
|
|
22
|
+
* result in [Section[Heading, Text, Image], Section[Text], Section[Text]].
|
|
23
|
+
*/
|
|
24
|
+
export function splitSection(mdast) {
|
|
25
|
+
// obtain the index of all thematicBreak nodes from the list of children
|
|
26
|
+
const thematicBreakIndexes = mdast.children
|
|
27
|
+
.filter((node) => node.type === 'thematicBreak')
|
|
28
|
+
// then get their index in the list of children
|
|
29
|
+
.map((node) => mdast.children.indexOf(node));
|
|
30
|
+
|
|
31
|
+
// find pairwise permutations of spaces between blocks
|
|
32
|
+
// include the very start and end of the document
|
|
33
|
+
const starts = [0, ...thematicBreakIndexes];
|
|
34
|
+
const ends = [...thematicBreakIndexes, mdast.children.length];
|
|
35
|
+
|
|
36
|
+
// content.mdast.children = _.zip(starts, ends)
|
|
37
|
+
const nodes = starts
|
|
38
|
+
.map((k, i) => [k, ends[i]])
|
|
39
|
+
// but filter out empty section
|
|
40
|
+
.filter(([start, end]) => start !== end);
|
|
41
|
+
// then return all nodes that are in between
|
|
42
|
+
|
|
43
|
+
mdast.children = nodes.map(([start, end]) => {
|
|
44
|
+
// skip 'thematicBreak' nodes
|
|
45
|
+
const index = mdast.children[start].type === 'thematicBreak' ? start + 1 : start;
|
|
46
|
+
return {
|
|
47
|
+
type: 'section',
|
|
48
|
+
children: mdast.children.slice(index, end),
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// unwrap sole section directly on the root
|
|
53
|
+
// if (mdast.children.length === 1 && mdast.children[0].type === 'section') {
|
|
54
|
+
// mdast.children = mdast.children[0].children;
|
|
55
|
+
// }
|
|
56
|
+
|
|
57
|
+
return mdast;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function wrapParagraphs(mdast) {
|
|
61
|
+
const sections = mdast.children.filter((node) => node.type === 'section');
|
|
62
|
+
|
|
63
|
+
sections.forEach((section) => {
|
|
64
|
+
const { children } = section;
|
|
65
|
+
const newChildren = [];
|
|
66
|
+
let paragraphGroup = [];
|
|
67
|
+
|
|
68
|
+
// process.stdout.write('==================================================\n');
|
|
69
|
+
// process.stdout.write(inspect(section));
|
|
70
|
+
// process.stdout.write('\n');
|
|
71
|
+
// process.stdout.write('==================================================\n');
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < children.length; i += 1) {
|
|
74
|
+
const node = children[i];
|
|
75
|
+
|
|
76
|
+
// Group paragraph and heading (depth >= 3) into the same paragraphWrapper
|
|
77
|
+
// group strong and emphasis into the same paragraphWrapper if they are not a button
|
|
78
|
+
if (node.type === 'paragraph'
|
|
79
|
+
|| node.type === 'list'
|
|
80
|
+
|| node.type === 'code'
|
|
81
|
+
|| (node.type === 'heading' && node.depth >= 3)
|
|
82
|
+
|| (node.type === 'strong' && (node.children.length !== 1 || (node.children.length === 1 && node.children[0].type !== 'link')))
|
|
83
|
+
|| (node.type === 'emphasis' && (node.children.length !== 1 || (node.children.length === 1 && node.children[0].type !== 'link')))
|
|
84
|
+
) {
|
|
85
|
+
paragraphGroup.push(node);
|
|
86
|
+
|
|
87
|
+
if (i === children.length - 1) {
|
|
88
|
+
newChildren.push({
|
|
89
|
+
type: 'paragraphWrapper',
|
|
90
|
+
children: paragraphGroup,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
} else {
|
|
94
|
+
if (paragraphGroup.length > 0) {
|
|
95
|
+
newChildren.push({
|
|
96
|
+
type: 'paragraphWrapper',
|
|
97
|
+
children: paragraphGroup,
|
|
98
|
+
});
|
|
99
|
+
paragraphGroup = [];
|
|
100
|
+
}
|
|
101
|
+
newChildren.push(node);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
section.children = newChildren;
|
|
105
|
+
|
|
106
|
+
// process.stdout.write('==================================================\n');
|
|
107
|
+
// process.stdout.write(inspect(section));
|
|
108
|
+
// process.stdout.write('\n');
|
|
109
|
+
// process.stdout.write('==================================================\n');
|
|
110
|
+
});
|
|
111
|
+
return mdast;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function unwrapImages(mdast) {
|
|
115
|
+
const sections = mdast.children.filter((node) => node.type === 'section');
|
|
116
|
+
|
|
117
|
+
sections.forEach((section) => {
|
|
118
|
+
const newChildren = [];
|
|
119
|
+
|
|
120
|
+
for (const node of section.children) {
|
|
121
|
+
if (node.type === 'paragraph') {
|
|
122
|
+
let currentText = [];
|
|
123
|
+
const images = [];
|
|
124
|
+
|
|
125
|
+
// Handle single child unwrapping for `link`, `strong`, and `em`
|
|
126
|
+
if (node.children.length === 1) {
|
|
127
|
+
const onlyChild = node.children[0];
|
|
128
|
+
if (['link', 'strong', 'emphasis'].includes(onlyChild.type)) {
|
|
129
|
+
// Move the single child outside the paragraph
|
|
130
|
+
newChildren.push(onlyChild);
|
|
131
|
+
// eslint-disable-next-line no-continue
|
|
132
|
+
continue; // Skip further processing of this paragraph node
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// process.stdout.write('==================================================\n');
|
|
137
|
+
// process.stdout.write(inspect(node));
|
|
138
|
+
// process.stdout.write('\n');
|
|
139
|
+
// process.stdout.write('==================================================\n');
|
|
140
|
+
|
|
141
|
+
// Traverse paragraph children to separate images and text nodes
|
|
142
|
+
for (let i = 0; i < node.children.length; i += 1) {
|
|
143
|
+
const child = node.children[i];
|
|
144
|
+
|
|
145
|
+
if (child.type === 'image') {
|
|
146
|
+
// If we have accumulated text, push it as a paragraph before the image
|
|
147
|
+
if (currentText.length > 0) {
|
|
148
|
+
newChildren.push({
|
|
149
|
+
type: 'paragraph',
|
|
150
|
+
children: currentText,
|
|
151
|
+
position: node.position,
|
|
152
|
+
});
|
|
153
|
+
currentText = [];
|
|
154
|
+
}
|
|
155
|
+
// Push the image outside the paragraph
|
|
156
|
+
images.push(child);
|
|
157
|
+
} else {
|
|
158
|
+
// Collect non-image children as part of the paragraph
|
|
159
|
+
currentText.push(child);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// If there are remaining text nodes, add them as a paragraph
|
|
164
|
+
if (currentText.length > 0) {
|
|
165
|
+
newChildren.push({
|
|
166
|
+
type: 'paragraph',
|
|
167
|
+
children: currentText,
|
|
168
|
+
position: node.position,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Move all images to their proper position in the parent tree
|
|
173
|
+
newChildren.push(...images);
|
|
174
|
+
} else {
|
|
175
|
+
// Non-paragraph nodes stay as they are
|
|
176
|
+
newChildren.push(node);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
section.children = newChildren;
|
|
181
|
+
});
|
|
182
|
+
return mdast;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function encodeHTMLEntities(str) {
|
|
186
|
+
return str ? str.replace(/&(?!amp;|lt;|gt;|quot;|apos;|#\d+;)/g, '&')
|
|
187
|
+
.replace(/</g, '<')
|
|
188
|
+
.replace(/"/g, '"') : '';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function encodeHtml(str) {
|
|
192
|
+
return Handlebars.escapeExpression(str);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Strip the new line characters from the given value. If the value is of Node, toString
|
|
197
|
+
* is then performed and then a replacement occurs. If the value is a string, a simple replace
|
|
198
|
+
* is performed.
|
|
199
|
+
* @param value the string or Node to strip newlines from.
|
|
200
|
+
* @return {string} the string with newlines replaced with spaces.
|
|
201
|
+
*/
|
|
202
|
+
export function stripNewlines(value) {
|
|
203
|
+
if (!value) {
|
|
204
|
+
return '';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (typeof value === 'string') {
|
|
208
|
+
return value.replace(/\n/g, ' ').trim();
|
|
209
|
+
}
|
|
210
|
+
return toString(value).replace(/\n/g, ' ').trim();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Sort properties by the following order: sling, jcr, other.
|
|
215
|
+
* @param {string} p1 - property 1
|
|
216
|
+
* @param {string} p2 - property 2
|
|
217
|
+
* @return {number} -1 if p1 is before p2, 1 if p2 is before p1, 0 if they are equal.
|
|
218
|
+
*/
|
|
219
|
+
export const sortJcrProperties = ([p1], [p2]) => {
|
|
220
|
+
if (p1.startsWith('sling:')) {
|
|
221
|
+
return -1;
|
|
222
|
+
}
|
|
223
|
+
if (p2.startsWith('sling:')) {
|
|
224
|
+
return 1;
|
|
225
|
+
}
|
|
226
|
+
if (p1.startsWith('jcr:')) {
|
|
227
|
+
return -1;
|
|
228
|
+
}
|
|
229
|
+
if (p2.startsWith('jcr:')) {
|
|
230
|
+
return 1;
|
|
231
|
+
}
|
|
232
|
+
return p1.localeCompare(p2);
|
|
233
|
+
};
|