-
Notifications
You must be signed in to change notification settings - Fork 242
Expand file tree
/
Copy pathMarkdown.js
More file actions
144 lines (134 loc) · 4.73 KB
/
Copy pathMarkdown.js
File metadata and controls
144 lines (134 loc) · 4.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
* Copyright 2020 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const PathResolver = require('../PathResolver');
const {fetchImageDimensions} = require('../fetchImageDimensions');
const {remove, insertAfter, createElement, firstChildByTag, nextNode} = require('../NodeUtils');
const DEFAULT_LAYOUT = 'intrinsic';
const LAYOUT_MIN_WIDTH = 320; // minimum mobile device screen width
/**
* Markdown - ensures markdown compatibility for input HTML
*
* This transformer adds out-of-the-box markdown support. This allows
* using AMP Optimizer to convert HTML documents created from Markdown
* files into valid AMP. A typical conversion flow would be:
*
* README.md => HTML => AMP Optimizer => valid AMP
*
* The only thing this transformer does is converting `<img>` tags into
* either `amp-img` or `amp-anim` tags. All other Markdown features are
* already supported by AMP. The transformer will try to resolve image
* dimensions from the actual files. Images larger than 320px will automatically
* get an intrinsic layout. For image detection to work, an optional dependency
* `probe-image-size` needs to be installed via NPM.
*
* This transformer supports the following options:
*
* - `markdown [Boolean]`: enables Markdown HTML support. Default is `false`.
* - `imageBasePath`: specifies a base path used to resolve an image during build,
* this can be a file system path or URL prefix.You can also pass a function
* `(imgSrc, params) => '../img/' + imgSrc` for dynamically calculating the image path.
*/
class Markdown {
constructor(config) {
this.log = config.log;
this.enabled = !!config.markdown;
// used for resolving image files
this.pathResolver = new PathResolver(config.imageBasePath);
}
async transform(tree, params) {
if (!this.enabled) {
return;
}
const html = firstChildByTag(tree, 'html');
if (!html) {
return;
}
const body = firstChildByTag(html, 'body');
if (!body) {
return;
}
let node = body;
const promises = [];
while (node) {
const tmpNode = nextNode(node);
if (node.tagName === 'img') {
promises.push(this.transformImg(node, params));
}
if(node.tagName === 'picture') {
promises.push(this.transformPicture(node, params));
}
node = tmpNode;
}
return Promise.all(promises);
}
async transformPicture(pictureNode, params) {
const imgNode = firstChildByTag(pictureNode, 'img');
if (!imgNode) {
return;
}
const src = imgNode.attribs && imgNode.attribs.src;
if (!src) {
return;
}
const resolvedSrc = this.pathResolver.resolve(src, params);
let dimensions;
try {
dimensions = await fetchImageDimensions(resolvedSrc);
} catch (error) {
this.log.warn(error.message);
// don't convert images we cannot resolve
return;
}
const ampImgOrAmpAnim = this.createAmpImgOrAmpAnim(dimensions, imgNode);
insertAfter(pictureNode.parent, ampImgOrAmpAnim, pictureNode);
remove(pictureNode);
}
async transformImg(imgNode, params) {
const src = imgNode.attribs && imgNode.attribs.src;
if (!src) {
return;
}
const resolvedSrc = this.pathResolver.resolve(src, params);
let dimensions;
try {
dimensions = await fetchImageDimensions(resolvedSrc);
} catch (error) {
this.log.warn(error.message);
// don't convert images we cannot resolve
return;
}
const ampImgOrAmpAnim = this.createAmpImgOrAmpAnim(dimensions, imgNode);
insertAfter(imgNode.parent, ampImgOrAmpAnim, imgNode);
remove(imgNode);
}
createAmpImgOrAmpAnim(dimensions, imgNode) {
const ampType = dimensions.type === 'gif' ? 'amp-anim' : 'amp-img';
const ampNode = createElement(ampType, imgNode.attribs);
// keep height and width if already specified
ampNode.attribs.width = imgNode.attribs.width || String(dimensions.width);
ampNode.attribs.height = imgNode.attribs.height || String(dimensions.height);
this.addLayout(ampNode, dimensions);
return ampNode;
}
addLayout(node, dimensions) {
if (dimensions.width < LAYOUT_MIN_WIDTH) {
return;
}
node.attribs.layout = DEFAULT_LAYOUT;
}
}
module.exports = Markdown;