@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.
Files changed (43) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/CODE_OF_CONDUCT.md +74 -0
  3. package/CONTRIBUTING.md +74 -0
  4. package/LICENSE.txt +264 -0
  5. package/README.md +27 -0
  6. package/baseline-tests.sh +45 -0
  7. package/package.json +81 -0
  8. package/src/cli/convert2jcr.js +128 -0
  9. package/src/index.d.ts +16 -0
  10. package/src/index.js +15 -0
  11. package/src/md2jcr/index.d.ts +20 -0
  12. package/src/md2jcr/index.js +36 -0
  13. package/src/mdast2jcr/domain/Component.js +66 -0
  14. package/src/mdast2jcr/domain/Definitions.js +78 -0
  15. package/src/mdast2jcr/domain/FieldGroup.js +85 -0
  16. package/src/mdast2jcr/domain/FieldGroupFieldResolver.js +96 -0
  17. package/src/mdast2jcr/domain/ModelHelper.js +123 -0
  18. package/src/mdast2jcr/domain/Models.js +46 -0
  19. package/src/mdast2jcr/hast-table-cell-handler.js +48 -0
  20. package/src/mdast2jcr/hast-table-handler.js +126 -0
  21. package/src/mdast2jcr/hb/helpers/encode-helper.js +18 -0
  22. package/src/mdast2jcr/hb/helpers/name-helper.js +61 -0
  23. package/src/mdast2jcr/hb/helpers/page-helper.js +109 -0
  24. package/src/mdast2jcr/hb/helpers/section-helper.js +53 -0
  25. package/src/mdast2jcr/hb/helpers/which-partial-helper.js +22 -0
  26. package/src/mdast2jcr/hb/partials/blockquote.js +16 -0
  27. package/src/mdast2jcr/hb/partials/emphasis.js +34 -0
  28. package/src/mdast2jcr/hb/partials/grid-table.js +495 -0
  29. package/src/mdast2jcr/hb/partials/heading.js +53 -0
  30. package/src/mdast2jcr/hb/partials/image.js +24 -0
  31. package/src/mdast2jcr/hb/partials/link.js +33 -0
  32. package/src/mdast2jcr/hb/partials/paragraph.js +30 -0
  33. package/src/mdast2jcr/hb/partials/strong.js +33 -0
  34. package/src/mdast2jcr/hb/partials/supports/image.js +27 -0
  35. package/src/mdast2jcr/hb/partials/supports/link.js +46 -0
  36. package/src/mdast2jcr/hb/partials/table.js +16 -0
  37. package/src/mdast2jcr/index.d.ts +122 -0
  38. package/src/mdast2jcr/index.js +94 -0
  39. package/src/mdast2jcr/mdast-download-images.js +165 -0
  40. package/src/mdast2jcr/mdast-sanitize-html.js +233 -0
  41. package/src/mdast2jcr/templates/page-template.js +27 -0
  42. package/src/mdast2jcr/utils/mdast.js +47 -0
  43. package/src/mdast2jcr/utils.js +233 -0
@@ -0,0 +1,46 @@
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
+ * @typedef {import('../mdast2jcr/index.d.ts').ModelDef} Model
15
+ * @typedef {import('../mdast2jcr/index.d.ts').DefinitionDef} Definition
16
+ * @typedef {import('../mdast2jcr/index.d.ts').CollapsedFields} CollapsedFields
17
+ * @typedef {import('../mdast2jcr/index.d.ts').FieldDef} Field
18
+ */
19
+
20
+ /**
21
+ * Get the model id from the array of models.
22
+ * @param {Array<Model>} models The array of models.
23
+ * @param {string} modelId The model id.
24
+ * @return {Model}
25
+ */
26
+ function findModelById(models, modelId) {
27
+ if (!models || Object.keys(models)?.length === 0) {
28
+ throw new Error('No models are provided. Check your model\'s JSON file.');
29
+ }
30
+ return models.find((model) => model.id === modelId);
31
+ }
32
+
33
+ /**
34
+ * Given a model, return the field with the given name.
35
+ * @param {Model} model The model.
36
+ * @param {string} fieldName The name of the field to get.
37
+ * @return {Field} field The field or undefined if not found.
38
+ */
39
+ function getField(model, fieldName) {
40
+ return model.fields.find((field) => field.name === fieldName);
41
+ }
42
+
43
+ export {
44
+ getField,
45
+ findModelById,
46
+ };
@@ -0,0 +1,48 @@
1
+ /*
2
+ * Copyright 2022 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
+ copied and adapted (adding align) from
15
+ https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/table-cell.js
16
+ */
17
+
18
+ 'use strict';
19
+
20
+ export default function cell(state, node) {
21
+ const wrap = state.wrapText;
22
+
23
+ // eslint-disable-next-line no-param-reassign
24
+ state.wrapText = false;
25
+
26
+ const result = {
27
+ type: 'tableCell',
28
+ children: state.all(node),
29
+ };
30
+ state.patch(node, result);
31
+
32
+ if (node.properties?.rowSpan || node.properties?.colSpan || node.properties?.align) {
33
+ const data = result.data || (result.data = {});
34
+ if (node.properties.rowSpan) {
35
+ data.rowSpan = node.properties.rowSpan;
36
+ }
37
+ if (node.properties.colSpan) {
38
+ data.colSpan = node.properties.colSpan;
39
+ }
40
+ if (node.properties.align) {
41
+ data.align = node.properties.align;
42
+ }
43
+ }
44
+ // eslint-disable-next-line no-param-reassign
45
+ state.wrapText = wrap;
46
+
47
+ return result;
48
+ }
@@ -0,0 +1,126 @@
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
+
13
+ 'use strict';
14
+
15
+ /*
16
+ copied and adapted from
17
+ https://github.com/syntax-tree/hast-util-to-mdast/blob/main/lib/handlers/table.js
18
+ */
19
+
20
+ // Ensure the cells in a row are properly structured.
21
+ function toCells(children) {
22
+ const nodes = [];
23
+ let queue;
24
+
25
+ children.forEach((node) => {
26
+ if (node.type === 'tableCell') {
27
+ if (queue) {
28
+ // eslint-disable-next-line no-param-reassign
29
+ node.children = queue.concat(node.children);
30
+ queue = undefined;
31
+ }
32
+
33
+ nodes.push(node);
34
+ } else {
35
+ if (!queue) {
36
+ queue = [];
37
+ }
38
+ queue.push(node);
39
+ }
40
+ });
41
+
42
+ if (queue) {
43
+ let node = nodes[nodes.length - 1];
44
+
45
+ if (!node) {
46
+ node = { type: 'tableCell', children: [] };
47
+ nodes.push(node);
48
+ }
49
+
50
+ node.children = node.children.concat(queue);
51
+ }
52
+
53
+ return nodes;
54
+ }
55
+
56
+ // Ensure the rows are properly structured.
57
+ function toRows(children) {
58
+ const nodes = [];
59
+ let queue;
60
+
61
+ children.forEach((node) => {
62
+ if (node.type === 'tableRow') {
63
+ if (queue) {
64
+ // eslint-disable-next-line no-param-reassign
65
+ node.children = queue.concat(node.children);
66
+ queue = undefined;
67
+ }
68
+
69
+ nodes.push(node);
70
+ } else {
71
+ if (!queue) queue = [];
72
+ queue.push(node);
73
+ }
74
+ });
75
+
76
+ if (queue) {
77
+ const node = nodes[nodes.length - 1];
78
+ node.children = node.children.concat(queue);
79
+ }
80
+ nodes.forEach((node) => {
81
+ // eslint-disable-next-line no-param-reassign
82
+ node.children = toCells(node.children);
83
+ });
84
+
85
+ return nodes;
86
+ }
87
+
88
+ export default function table(state, node) {
89
+ const mdNode = {
90
+ type: 'table',
91
+ children: toRows(state.all(node)),
92
+ // clean up table in respect to row and colspan and compute alignments
93
+ align: [],
94
+ maxCols: 0,
95
+ };
96
+
97
+ // compute the number of cells in each row, respecting the row and col spans.
98
+ const pendingRowSpans = [];
99
+ for (const row of mdNode.children) {
100
+ row.numCols = pendingRowSpans.shift() || 0;
101
+ for (const cell of row.children) {
102
+ const rowSpan = Number.parseInt(cell.data?.rowSpan || '1', 10);
103
+ const colSpan = Number.parseInt(cell.data?.colSpan || '1', 10);
104
+ if (cell.data?.align && !mdNode.align[row.numCols]) {
105
+ mdNode.align[row.numCols] = cell.data.align;
106
+ }
107
+ row.numCols += colSpan;
108
+ if (rowSpan > 1) {
109
+ for (let i = 0; i < rowSpan - 1; i += 1) {
110
+ pendingRowSpans[i] = (pendingRowSpans[i] || 0) + colSpan;
111
+ }
112
+ }
113
+ }
114
+ mdNode.maxCols = Math.max(mdNode.maxCols, row.numCols);
115
+ }
116
+
117
+ // add empty cells if needed
118
+ for (const row of mdNode.children) {
119
+ for (let i = row.numCols; i < mdNode.maxCols; i += 1) {
120
+ row.children.push({ type: 'tableCell', children: [] });
121
+ }
122
+ delete row.numCols;
123
+ }
124
+
125
+ return mdNode;
126
+ }
@@ -0,0 +1,18 @@
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
+ import Handlebars from 'handlebars';
13
+
14
+ function encodeHelper(options) {
15
+ return Handlebars.Utils.escapeExpression(options.fn(this));
16
+ }
17
+
18
+ export default encodeHelper;
@@ -0,0 +1,61 @@
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
+ * An object that keeps track of the number of times a certain node has been
15
+ * added. Each time a node is added, the counter is incremented, thereby
16
+ * generating a unique name for the node.
17
+ */
18
+ const nameCounter = [];
19
+
20
+ /**
21
+ * The name helper function keeps track of the number of times a certain node
22
+ * has been created, and returns a unique name for it. The format of the name
23
+ * is the name of the node followed by an underscore and a number. For example,
24
+ * if the name of the node is 'title', the first node will have no underscore or
25
+ * number. A second occurrence of the node will have the name '_1', and so on.
26
+ *
27
+ * Special handling is done for the 'section' node. The 'section' node is treated
28
+ * as a top level node, and the name counter is reset each time a 'section' node.
29
+ *
30
+ * @param name The node name (e.g. 'title', 'section')
31
+ * @returns {string} The unique name for the node (e.g. '', '_1', '_2')
32
+ * or an empty string if the node is unique.
33
+ */
34
+ function nameHelper(name) {
35
+ // reset the name counter if the node is a section or if the name counter is empty
36
+ // we could reset the name counter and empty all the counters, but it's cool
37
+ // to see the counts for debugging
38
+ if (name === 'section' || !nameCounter.length) {
39
+ nameCounter.push({});
40
+ }
41
+
42
+ // treat sections as a top level node and return the length of the name counter
43
+ if (name === 'section') {
44
+ return nameCounter.length > 1 ? `_${nameCounter.length - 1}` : '';
45
+ }
46
+
47
+ // get the current counter
48
+ const currentCounter = nameCounter[nameCounter.length - 1];
49
+
50
+ // increment the counter for the node name
51
+ currentCounter[name] = (currentCounter[name] || 0) + 1;
52
+
53
+ // return the counter value if it is greater than 1 (the node is not unique)
54
+ return currentCounter[name] > 1 ? `_${currentCounter[name] - 1}` : '';
55
+ }
56
+
57
+ export function nameReset() {
58
+ nameCounter.length = 0;
59
+ }
60
+
61
+ export default nameHelper;
@@ -0,0 +1,109 @@
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
+ import { find } from 'unist-util-find';
13
+ import { toString } from 'mdast-util-to-string';
14
+ import { findModelById, getField } from '../../domain/Models.js';
15
+ import { findAll } from '../../utils/mdast.js';
16
+ import { encodeHTMLEntities, stripNewlines } from '../../utils.js';
17
+ import link from '../partials/supports/link.js';
18
+ import image from '../partials/supports/image.js';
19
+
20
+ function buildPageMetadata(table, models) {
21
+ const aemMapping = {
22
+ description: 'jcr:description',
23
+ canonical: 'cq:canonicalUrl',
24
+ robots: 'cq:robotsTags',
25
+ title: 'jcr:title',
26
+ };
27
+
28
+ const model = findModelById(models, 'page-metadata');
29
+
30
+ const isMultiField = (fieldName) => {
31
+ const field = getField(model, fieldName.name);
32
+ return field && (field.component === 'multiselect' || field.component === 'aem-tag' || field.component === 'checkbox-group' || field.multi);
33
+ };
34
+
35
+ const metadata = {};
36
+ const [, ...rows] = findAll(table, (node) => node.type === 'gtRow', false);
37
+ rows.forEach((row) => {
38
+ const cells = findAll(row, (node) => node.type === 'gtCell', true);
39
+ const key = toString(cells[0]);
40
+
41
+ // if the model has the field, then add it to the metadata
42
+ const field = getField(model, key);
43
+ if (field) {
44
+ // if the field is an image then we need to dig into the row to find the image
45
+ if (find(row.children[1], { type: 'image' })) {
46
+ const { url } = image.getProperties(row);
47
+ metadata['xwalk:imageReference'] = url;
48
+ } else if (link.supports(row)) {
49
+ const { href } = link.getProperties(row);
50
+ metadata[field.name] = href;
51
+ } else {
52
+ let value = stripNewlines(cells[1]);
53
+ if (isMultiField(field)) {
54
+ value = value.split(',').map((v) => encodeHTMLEntities(v.trim()));
55
+ value = value.length > 0 ? `[${value.join(',')}]` : '';
56
+ }
57
+ metadata[field.name] = value;
58
+ }
59
+ }
60
+ });
61
+
62
+ // now go through the metadata and map the fields to aem fields
63
+ const mappedMetadata = {};
64
+ Object.entries(metadata).forEach(([key, value]) => {
65
+ const mappedKey = aemMapping[key] || key;
66
+ mappedMetadata[mappedKey] = value;
67
+ });
68
+
69
+ return mappedMetadata;
70
+ }
71
+
72
+ function pageHelper(options) {
73
+ const {
74
+ models,
75
+ ...mdast
76
+ } = options.data.root;
77
+
78
+ const attributes = {
79
+ 'cq:template': '/libs/core/franklin/templates/page',
80
+ 'sling:resourceType': 'core/franklin/components/page/v1/page',
81
+ 'jcr:primaryType': 'cq:PageContent',
82
+ };
83
+
84
+ const tables = findAll(mdast, (node) => node.type === 'gridTable', false);
85
+ if (tables) {
86
+ tables.forEach((table) => {
87
+ const cell = find(table, { type: 'gtCell' });
88
+ if (cell) {
89
+ const isMetadata = toString(cell).toLowerCase() === 'metadata';
90
+ if (isMetadata) {
91
+ const meta = buildPageMetadata(table, models, attributes);
92
+ Object.assign(attributes, meta);
93
+ }
94
+ }
95
+ });
96
+ }
97
+
98
+ // pull the image reference out of the attributes
99
+ const { 'xwalk:imageReference': imageRef, ...properties } = attributes;
100
+ const attributesStr = Object.entries(properties).map(([k, v]) => `${k}="${v}"`).join(' ');
101
+
102
+ return `<jcr:content ${attributesStr}>
103
+ ${options.fn(this)}
104
+ ${imageRef ? `<image jcr:primaryType="nt:unstructured" fileReference="${imageRef}" />` : ''}
105
+ </jcr:content>
106
+ `;
107
+ }
108
+
109
+ export default pageHelper;
@@ -0,0 +1,53 @@
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
+ import Handlebars from 'handlebars';
13
+ import { find } from 'unist-util-find';
14
+ import { toString } from 'mdast-util-to-string';
15
+ import { findAll } from '../../utils/mdast.js';
16
+
17
+ function sectionHelper(index, children, options) {
18
+ const attributes = {
19
+ 'sling:resourceType': 'core/franklin/components/section/v1/section',
20
+ 'jcr:primaryType': 'nt:unstructured',
21
+ };
22
+
23
+ const uniqueName = Handlebars.helpers.nameHelper.call(this, 'section');
24
+
25
+ // find the table that has a gtHeader that has a header value that contains 'section-metadata'
26
+ for (const child of children) {
27
+ if (child.type === 'gridTable') {
28
+ const cell = find(child, { type: 'gtCell' });
29
+ if (cell) {
30
+ const isMetadata = toString(cell).toLowerCase().replaceAll(' ', '-') === 'section-metadata';
31
+ if (isMetadata) {
32
+ const [, ...rows] = findAll(child, (n) => n.type === 'gtRow', false);
33
+ for (const row of rows) {
34
+ const cells = findAll(row, (n) => n.type === 'gtCell', true);
35
+ const key = toString(cells[0]);
36
+ const value = toString(cells[1]);
37
+ if (key === 'Style') {
38
+ attributes.classes = value;
39
+ } else {
40
+ attributes[`data-${key.toLowerCase().replaceAll(' ', '-')}`] = value;
41
+ }
42
+ }
43
+ break;
44
+ }
45
+ }
46
+ }
47
+ }
48
+
49
+ const attributesStr = Object.entries(attributes).map(([k, v]) => `${k}="${v}"`).join(' ');
50
+ return `<section${uniqueName} ${attributesStr}>${options.fn(this)}</section${uniqueName}>`;
51
+ }
52
+
53
+ export default sectionHelper;
@@ -0,0 +1,22 @@
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
+ * Partial Resolver will return the partial helper that's associated with the
14
+ * type.
15
+ * @param type the type of partial
16
+ * @returns {string} the partial type
17
+ */
18
+ function whichPartialHelper(type) {
19
+ return type;
20
+ }
21
+
22
+ export default whichPartialHelper;
@@ -0,0 +1,16 @@
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
+ function blockQuotePartial() {
13
+ return '<section></section>\n';
14
+ }
15
+
16
+ export default blockQuotePartial;
@@ -0,0 +1,34 @@
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
+ import Handlebars from 'handlebars';
14
+
15
+ function emphasis(context) {
16
+ const uniqueName = Handlebars.helpers.nameHelper.call(context, 'button');
17
+ const { children: [child] } = context;
18
+ const { title, url, children: [{ value }] } = child;
19
+ const attributes = {
20
+ 'sling:resourceType': 'core/franklin/components/button/v1/button',
21
+ 'jcr:primaryType': 'nt:unstructured',
22
+ link: url,
23
+ linkText: value,
24
+ linkType: 'secondary',
25
+ };
26
+ if (title != null) {
27
+ attributes.linkTitle = title;
28
+ }
29
+
30
+ const attributesStr = Object.entries(attributes).map(([k, v]) => `${k}="${v}"`).join(' ');
31
+ return `<button${uniqueName} ${attributesStr} />`;
32
+ }
33
+
34
+ export default emphasis;