@adobe/helix-html-pipeline 1.1.3 → 1.3.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 +21 -0
- package/package.json +12 -10
- package/src/PipelineContent.d.ts +8 -7
- package/src/PipelineContent.js +2 -0
- package/src/PipelineResponse.d.ts +6 -1
- package/src/PipelineState.d.ts +3 -1
- package/src/PipelineState.js +1 -0
- package/src/forms-pipe.js +160 -0
- package/src/html-pipe.js +7 -0
- package/src/index.d.ts +13 -0
- package/src/index.js +2 -0
- package/src/options-pipe.js +37 -0
- package/src/steps/add-heading-ids.js +14 -13
- package/src/steps/create-page-blocks.js +28 -27
- package/src/steps/create-pictures.js +16 -12
- package/src/steps/extract-metadata.js +61 -44
- package/src/steps/fix-sections.js +8 -9
- package/src/steps/get-metadata.js +5 -4
- package/src/steps/make-html.js +3 -14
- package/src/steps/removeHlxProps.js +9 -10
- package/src/steps/render.js +68 -116
- package/src/steps/rewrite-blob-images.js +6 -24
- package/src/steps/rewrite-icons.js +30 -44
- package/src/steps/stringify-response.js +11 -11
- package/src/steps/utils.js +26 -4
- package/src/utils/{table-handler.js → hast-utils.js} +13 -15
- package/src/utils/heading-handler.js +11 -24
- package/src/utils/mdast-to-hast.js +60 -0
- package/src/utils/path.js +4 -1
- package/src/utils/section-handler.js +6 -4
- package/src/utils/hast-util-to-dom.js +0 -190
- package/src/utils/icon-handler.js +0 -40
- package/src/utils/link-handler.js +0 -25
- package/src/utils/mdast-to-vdom.js +0 -323
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2018 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 { selectAll } from 'unist-util-select';
|
|
14
|
-
import { toHast as mdast2hast, defaultHandlers } from 'mdast-util-to-hast';
|
|
15
|
-
import { toHtml as hast2html } from 'hast-util-to-html';
|
|
16
|
-
import { JSDOM } from 'jsdom';
|
|
17
|
-
|
|
18
|
-
import toDOM from './hast-util-to-dom.js';
|
|
19
|
-
import HeadingHandler from './heading-handler.js';
|
|
20
|
-
import link from './link-handler.js';
|
|
21
|
-
import table from './table-handler.js';
|
|
22
|
-
import icon from './icon-handler.js';
|
|
23
|
-
import section from './section-handler.js';
|
|
24
|
-
|
|
25
|
-
const types = [
|
|
26
|
-
'root',
|
|
27
|
-
'paragraph',
|
|
28
|
-
'text',
|
|
29
|
-
'heading',
|
|
30
|
-
'thematicBreak',
|
|
31
|
-
'blockquote',
|
|
32
|
-
'list',
|
|
33
|
-
'listItem',
|
|
34
|
-
'table',
|
|
35
|
-
'tableRow',
|
|
36
|
-
'tableCell',
|
|
37
|
-
'html',
|
|
38
|
-
'code',
|
|
39
|
-
'yaml',
|
|
40
|
-
'definition',
|
|
41
|
-
'footnoteDefinition',
|
|
42
|
-
'emphasis',
|
|
43
|
-
'strong',
|
|
44
|
-
'delete',
|
|
45
|
-
'inlineCode',
|
|
46
|
-
'break',
|
|
47
|
-
'link',
|
|
48
|
-
'image',
|
|
49
|
-
'linkReference',
|
|
50
|
-
'imageReference',
|
|
51
|
-
'footnote',
|
|
52
|
-
'footnoteReference',
|
|
53
|
-
'embed',
|
|
54
|
-
'dataEmbed',
|
|
55
|
-
'section',
|
|
56
|
-
'icon',
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @typedef {function(parent, tagName, attributes, children)} handlerFunction
|
|
61
|
-
* @param {Node} parent the root node to append the new dom node to
|
|
62
|
-
* @param {string} tagName name of the new tag
|
|
63
|
-
* @param {object} attributes HTML attributes as key-value pairs
|
|
64
|
-
* @param {Node[]} children list of children
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Utility class that transforms an MDAST (Markdown) node into a (virtual) DOM
|
|
69
|
-
* representation of the same content.
|
|
70
|
-
*/
|
|
71
|
-
export default class VDOMTransformer {
|
|
72
|
-
/**
|
|
73
|
-
* Initializes the transformer with a Markdown document or fragment of a document
|
|
74
|
-
* @param {Node} mdast the markdown AST node to start the transformation from.
|
|
75
|
-
* @param {object} options options for custom transformers
|
|
76
|
-
*/
|
|
77
|
-
constructor(mdast, options) {
|
|
78
|
-
this._matchers = [];
|
|
79
|
-
this._handlers = {};
|
|
80
|
-
this._options = {};
|
|
81
|
-
|
|
82
|
-
// go over all handlers that have been defined
|
|
83
|
-
|
|
84
|
-
const that = this;
|
|
85
|
-
// use our own handle function for every known node type
|
|
86
|
-
types.map((type) => {
|
|
87
|
-
this._handlers[type] = (cb, node, parent) => VDOMTransformer.handle(cb, node, parent, that);
|
|
88
|
-
return true;
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (mdast) {
|
|
92
|
-
this.withMdast(mdast);
|
|
93
|
-
}
|
|
94
|
-
this.withOptions(options);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
withOptions(options = {}) {
|
|
98
|
-
this._options = Object.assign(this._options, options);
|
|
99
|
-
|
|
100
|
-
this._headingHandler = new HeadingHandler(options.slugger);
|
|
101
|
-
this.match('heading', this._headingHandler.handler());
|
|
102
|
-
this.match('link', link(this._options));
|
|
103
|
-
this.match('icon', icon());
|
|
104
|
-
this.match('table', table());
|
|
105
|
-
this.match('section', section());
|
|
106
|
-
this.match('html', (h, node) => {
|
|
107
|
-
if (node.value.startsWith('<!--')) {
|
|
108
|
-
return h.augment(node, {
|
|
109
|
-
type: 'comment',
|
|
110
|
-
value: node.value.substring(4, node.value.length - 3),
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
this._hasRaw = true;
|
|
114
|
-
const frag = JSDOM.fragment(node.value);
|
|
115
|
-
return h.augment(node, {
|
|
116
|
-
type: 'raw',
|
|
117
|
-
value: '', // we ignore the value here and treat it later in hast-util-to-dom
|
|
118
|
-
frag,
|
|
119
|
-
html: node.value,
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return this;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
withMdast(mdast) {
|
|
127
|
-
this._root = mdast;
|
|
128
|
-
|
|
129
|
-
return this;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* A mdast-util-to-hast handler function that applies matchers and
|
|
134
|
-
* falls back to the default mdast-util-to-hast handlers if no matchers
|
|
135
|
-
* apply
|
|
136
|
-
* @private
|
|
137
|
-
* @param {handlerFunction} cb
|
|
138
|
-
* @param {Node} node the MDAST node to transofrm
|
|
139
|
-
* @param {Node} parent the MDAST parent or root node for select expressions
|
|
140
|
-
* @param {VDOMTransformer} that the MDAST to VDOM transformer
|
|
141
|
-
* @returns {Node} a HTAST representation of the `node` input
|
|
142
|
-
*/
|
|
143
|
-
static handle(cb, node, parent, that) {
|
|
144
|
-
// get the function that handles this node type
|
|
145
|
-
// this will fall back to the default if none matches
|
|
146
|
-
const handlefn = that.matches(node);
|
|
147
|
-
|
|
148
|
-
// process the node
|
|
149
|
-
|
|
150
|
-
const result = handlefn(cb, node, parent);
|
|
151
|
-
if (result && typeof result === 'string') {
|
|
152
|
-
throw new Error('returning string from a handler is not supported yet.');
|
|
153
|
-
} else if (result && typeof result === 'object' && result.outerHTML) {
|
|
154
|
-
throw new Error('returning a DOM element from a handler is not supported yet.');
|
|
155
|
-
}
|
|
156
|
-
return result;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Returns the default handler for a given node type
|
|
161
|
-
* @param {Node} node an MDAST node
|
|
162
|
-
* @returns {handlerFunction} the default handler function
|
|
163
|
-
*/
|
|
164
|
-
static default(node) {
|
|
165
|
-
// use the default handler from mdast-util-to-hast
|
|
166
|
-
return defaultHandlers[node.type];
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* A predicate function that filters MDAST nodes
|
|
171
|
-
* @typedef {function(node)} matcherFunction
|
|
172
|
-
* @param {Node} node an MDAST node
|
|
173
|
-
* @returns {boolean} true for matching nodes
|
|
174
|
-
*/
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Registers a handler function for nodes that match either a select expression
|
|
178
|
-
* or a matcher predicate function. The `matcher` will be evaluated against every
|
|
179
|
-
* node in the MDAST. In cases where the `matcher` matches (returns true), the
|
|
180
|
-
* processor will be called with the current node.
|
|
181
|
-
* @param {(string|matcherFunction)} matcher either an unist-util-select expression
|
|
182
|
-
* or a predicate function
|
|
183
|
-
* @param {handlerFunction} processor the appropriate handler function to handle matching types.
|
|
184
|
-
* @returns {VDOMTransformer} this, enabling chaining
|
|
185
|
-
*/
|
|
186
|
-
match(matcher, processor) {
|
|
187
|
-
const matchfn = typeof matcher === 'function' ? matcher : VDOMTransformer.matchfn(this._root, matcher);
|
|
188
|
-
this._matchers.push([matchfn, processor]);
|
|
189
|
-
return this;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Finds an appropriate handler for a given MDAST node
|
|
194
|
-
* @private
|
|
195
|
-
* @param {Node} node an MDAST node
|
|
196
|
-
* @returns {handlerFunction} a handler function to process the node with
|
|
197
|
-
*/
|
|
198
|
-
matches(node) {
|
|
199
|
-
// go through all matchers to find processors where matchfn matches
|
|
200
|
-
// start with most recently added processors
|
|
201
|
-
for (let i = this._matchers.length - 1; i >= 0; i -= 1) {
|
|
202
|
-
const [matchfn, processor] = this._matchers[i];
|
|
203
|
-
if (matchfn(node, this._root)) {
|
|
204
|
-
// return the first processor that matches
|
|
205
|
-
return processor;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// add the fallback processors
|
|
209
|
-
return VDOMTransformer.default(node);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Turns an unist-util-select expression into a matcher predicate function
|
|
214
|
-
* @private
|
|
215
|
-
* @param {Node} ast the MDAST root node to evaluated expressions against
|
|
216
|
-
* @param {string} pattern a CSS-like unist-util-select expression
|
|
217
|
-
* @returns {matcherFunction} a corresponding matcher function that returns true
|
|
218
|
-
* for nodes matching the pattern
|
|
219
|
-
*/
|
|
220
|
-
static matchfn(ast, pattern) {
|
|
221
|
-
// evaluating selectAll on a large tree for each handler is very expensive.
|
|
222
|
-
// use node name for simple element selectors
|
|
223
|
-
if (/^\w+$/.test(pattern)) {
|
|
224
|
-
return function match(node) {
|
|
225
|
-
return node.type === pattern;
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return function match(node, myast = ast) {
|
|
230
|
-
return selectAll(pattern, myast).indexOf(node) >= 0;
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Tries to sanitize inline HTML elements. The remark parser creates `raw` nodes for html.
|
|
236
|
-
* In case of inline html, those are not closed elements. since we generated the DOM already
|
|
237
|
-
* in the HTML handler, we get incomplete fragments, missing the inner HTML. This method tries
|
|
238
|
-
* to fix this. It doesn't support inner markdown-elements, though.
|
|
239
|
-
*
|
|
240
|
-
* @param {object} node HAST node
|
|
241
|
-
*/
|
|
242
|
-
static sanitizeInlineHTML(node) {
|
|
243
|
-
const stack = [];
|
|
244
|
-
for (let i = 0; i < node.children.length; i += 1) {
|
|
245
|
-
const child = node.children[i];
|
|
246
|
-
if (child.type === 'raw') {
|
|
247
|
-
if (child.frag.firstElementChild === null) {
|
|
248
|
-
if (stack.length === 0) {
|
|
249
|
-
// ignore unmatched inline elements
|
|
250
|
-
} else {
|
|
251
|
-
const last = stack.pop();
|
|
252
|
-
let html = '';
|
|
253
|
-
for (let j = last; j <= i; j += 1) {
|
|
254
|
-
const innerChild = node.children[j];
|
|
255
|
-
if (innerChild.type === 'raw') {
|
|
256
|
-
html += innerChild.html;
|
|
257
|
-
} else {
|
|
258
|
-
html += hast2html(innerChild);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
node.children[last].frag = JSDOM.fragment(html);
|
|
262
|
-
node.children[last].html = html;
|
|
263
|
-
node.children.splice(last + 1, i - last);
|
|
264
|
-
i = last;
|
|
265
|
-
}
|
|
266
|
-
} else {
|
|
267
|
-
stack.push(i);
|
|
268
|
-
}
|
|
269
|
-
} else if (child.children && child.children.length) {
|
|
270
|
-
VDOMTransformer.sanitizeInlineHTML(child);
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Turns the MDAST into a full DOM-like structure using JSDOM
|
|
277
|
-
* @returns {Document} a full DOM document
|
|
278
|
-
*/
|
|
279
|
-
getDocument() {
|
|
280
|
-
// mdast -> hast; hast -> DOM using JSDOM
|
|
281
|
-
const hast = mdast2hast(this._root, {
|
|
282
|
-
handlers: this._handlers,
|
|
283
|
-
allowDangerousHtml: true,
|
|
284
|
-
});
|
|
285
|
-
|
|
286
|
-
if (this._hasRaw) {
|
|
287
|
-
VDOMTransformer.sanitizeInlineHTML(hast);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const dom = new JSDOM();
|
|
291
|
-
const doc = dom.window.document;
|
|
292
|
-
const frag = toDOM(doc, hast, { fragment: true });
|
|
293
|
-
|
|
294
|
-
if (frag.nodeName === '#document') {
|
|
295
|
-
// this only happens if it's an empty markdown document, so just ignore
|
|
296
|
-
} else {
|
|
297
|
-
doc.body.appendChild(frag);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// add convenience function to serialize entire document. this is to make it similar to the
|
|
301
|
-
// document created in html-to-vdom.
|
|
302
|
-
doc.serialize = dom.serialize.bind(dom);
|
|
303
|
-
|
|
304
|
-
// this is a bit a hack to pass the JSDOM instance along, so that other module can use it.
|
|
305
|
-
// this ensures that other modules can parse documents and fragments that are compatible
|
|
306
|
-
// with this document
|
|
307
|
-
Object.defineProperty(doc, 'JSDOM', {
|
|
308
|
-
enumerable: false,
|
|
309
|
-
writable: false,
|
|
310
|
-
value: JSDOM,
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
// this is another hack to pass the respective window instance along. this is to ensure that
|
|
314
|
-
// the shared prototypes can be used to check node instances (see jsdom 16.x release)
|
|
315
|
-
Object.defineProperty(doc, 'window', {
|
|
316
|
-
enumerable: false,
|
|
317
|
-
writable: false,
|
|
318
|
-
value: dom.window,
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
return doc;
|
|
322
|
-
}
|
|
323
|
-
}
|