Skip to content

Commit 0146fc6

Browse files
jlengstorfpieh
authored andcommitted
fix(gatsby-remark-images): don’t add links if image is already linked (#6982)
* fix(gatsby-remark-images): don’t add links if image is already linked fixes #6980 Co-authored-by: @pieh * fix: handle weird nesting and mixed MD/HTML * fix: remove bad check for parent links
1 parent ed191ba commit 0146fc6

File tree

4 files changed

+169
-12
lines changed

4 files changed

+169
-12
lines changed

packages/gatsby-remark-images/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"is-relative-url": "^2.0.0",
1313
"lodash": "^4.17.4",
1414
"slash": "^1.0.0",
15-
"unist-util-select": "^1.5.0"
15+
"unist-util-select": "^1.5.0",
16+
"unist-util-visit-parents": "^2.0.1"
1617
},
1718
"devDependencies": {
1819
"@babel/cli": "7.0.0-beta.52",

packages/gatsby-remark-images/src/__tests__/__snapshots__/index.js.snap

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,75 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`it handles goofy nesting properly 1`] = `
4+
"
5+
<span
6+
class=\\"gatsby-resp-image-wrapper\\"
7+
style=\\"position: relative; display: block; ; max-width: 300px; margin-left: auto; margin-right: auto;\\"
8+
>
9+
<span
10+
class=\\"gatsby-resp-image-background-image\\"
11+
style=\\"padding-bottom: 133.33333333333331%; position: relative; bottom: 0; left: 0; background-image: url('url('data:image/png;base64, iVBORw)'); background-size: cover; display: block;\\"
12+
>
13+
<img
14+
class=\\"gatsby-resp-image-image\\"
15+
style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\"
16+
alt=\\"test\\"
17+
title=\\"\\"
18+
src=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg\\"
19+
srcset=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg, not-a-real-dir/images/this-image-already-has-a-link.jpeg\\"
20+
sizes=\\"(max-width: 650px) 100vw, 650px\\"
21+
/>
22+
</span>
23+
</span>
24+
"
25+
`;
26+
27+
exports[`it leaves images that are already linked alone 1`] = `
28+
"
29+
<span
30+
class=\\"gatsby-resp-image-wrapper\\"
31+
style=\\"position: relative; display: block; ; max-width: 300px; margin-left: auto; margin-right: auto;\\"
32+
>
33+
<span
34+
class=\\"gatsby-resp-image-background-image\\"
35+
style=\\"padding-bottom: 133.33333333333331%; position: relative; bottom: 0; left: 0; background-image: url('url('data:image/png;base64, iVBORw)'); background-size: cover; display: block;\\"
36+
>
37+
<img
38+
class=\\"gatsby-resp-image-image\\"
39+
style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\"
40+
alt=\\"img\\"
41+
title=\\"\\"
42+
src=\\"not-a-real-dir/image/my-image.jpg\\"
43+
srcset=\\"not-a-real-dir/image/my-image.jpg, not-a-real-dir/image/my-image.jpg\\"
44+
sizes=\\"(max-width: 650px) 100vw, 650px\\"
45+
/>
46+
</span>
47+
</span>
48+
"
49+
`;
50+
51+
exports[`it leaves linked HTML img tags alone 1`] = `
52+
"<a href=\\"https://example.org\\">
53+
54+
<span class=\\"gatsby-resp-image-wrapper\\" style=\\"position: relative; display: block; ; max-width: 300px; margin-left: auto; margin-right: auto;\\">
55+
<span class=\\"gatsby-resp-image-background-image\\" style=\\"padding-bottom: 133.33333333333331%; position: relative; bottom: 0; left: 0; background-image: url(&apos;url(&apos;data:image/png;base64, iVBORw)&apos;); background-size: cover; display: block;\\">
56+
<img class=\\"gatsby-resp-image-image\\" style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\" alt=\\"this image already has a link\\" title=\\"\\" src=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg\\" srcset=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg, not-a-real-dir/images/this-image-already-has-a-link.jpeg\\" sizes=\\"(max-width: 650px) 100vw, 650px\\">
57+
</span>
58+
</span>
59+
60+
</a>"
61+
`;
62+
63+
exports[`it leaves single-line linked HTML img tags alone 1`] = `
64+
"
65+
<span class=\\"gatsby-resp-image-wrapper\\" style=\\"position: relative; display: block; ; max-width: 300px; margin-left: auto; margin-right: auto;\\">
66+
<span class=\\"gatsby-resp-image-background-image\\" style=\\"padding-bottom: 133.33333333333331%; position: relative; bottom: 0; left: 0; background-image: url(&apos;url(&apos;data:image/png;base64, iVBORw)&apos;); background-size: cover; display: block;\\">
67+
<img class=\\"gatsby-resp-image-image\\" style=\\"width: 100%; height: 100%; margin: 0; vertical-align: middle; position: absolute; top: 0; left: 0; box-shadow: inset 0px 0px 0px 400px white;\\" alt=\\"this image already has a link\\" title=\\"\\" src=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg\\" srcset=\\"not-a-real-dir/images/this-image-already-has-a-link.jpeg, not-a-real-dir/images/this-image-already-has-a-link.jpeg\\" sizes=\\"(max-width: 650px) 100vw, 650px\\">
68+
</span>
69+
</span>
70+
"
71+
`;
72+
373
exports[`it transforms HTML img tags 1`] = `
474
"
575
<a class=\\"gatsby-resp-image-link\\" href=\\"not-a-real-dir/image/my-image.jpeg\\" style=\\"display: block\\" target=\\"_blank\\" rel=\\"noopener\\">

packages/gatsby-remark-images/src/__tests__/index.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,64 @@ test(`it leaves non-relative HTML img tags alone`, async () => {
146146
const nodes = await plugin(createPluginOptions(content, imagePath))
147147
expect(nodes[0].value).toBe(content)
148148
})
149+
150+
test(`it leaves images that are already linked alone`, async () => {
151+
const imagePath = `image/my-image.jpg`
152+
const content = `
153+
[![img](./${imagePath})](https://google.com)
154+
`
155+
156+
const nodes = await plugin(createPluginOptions(content, imagePath))
157+
const node = nodes.pop()
158+
159+
expect(node.type).toBe(`html`)
160+
expect(node.value).toMatchSnapshot()
161+
expect(node.value).not.toMatch(`<html>`)
162+
})
163+
164+
test(`it leaves linked HTML img tags alone`, async () => {
165+
const imagePath = `images/this-image-already-has-a-link.jpeg`
166+
167+
const content = `
168+
<a href="https://example.org">
169+
<img src="./${imagePath}">
170+
</a>
171+
`.trim()
172+
173+
const nodes = await plugin(createPluginOptions(content, imagePath))
174+
const node = nodes.pop()
175+
176+
expect(node.type).toBe(`html`)
177+
expect(node.value).toMatchSnapshot()
178+
expect(node.value).not.toMatch(`<html>`)
179+
})
180+
181+
test(`it leaves single-line linked HTML img tags alone`, async () => {
182+
const imagePath = `images/this-image-already-has-a-link.jpeg`
183+
184+
const content = `
185+
<a href="https://example.org"><img src="./${imagePath}"></a>
186+
`.trim()
187+
188+
const nodes = await plugin(createPluginOptions(content, imagePath))
189+
const node = nodes.pop()
190+
191+
expect(node.type).toBe(`html`)
192+
expect(node.value).toMatchSnapshot()
193+
expect(node.value).not.toMatch(`<html>`)
194+
})
195+
196+
test(`it handles goofy nesting properly`, async () => {
197+
const imagePath = `images/this-image-already-has-a-link.jpeg`
198+
199+
const content = `
200+
<a href="https://google.com">**![test](./${imagePath})**</a>
201+
`.trim()
202+
203+
const nodes = await plugin(createPluginOptions(content, imagePath))
204+
const node = nodes.pop()
205+
206+
expect(node.type).toBe(`html`)
207+
expect(node.value).toMatchSnapshot()
208+
expect(node.value).not.toMatch(`<html>`)
209+
})

packages/gatsby-remark-images/src/index.js

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
const select = require(`unist-util-select`)
1+
// const select = require(`unist-util-select`)
2+
const visitWithParents = require(`unist-util-visit-parents`)
23
const path = require(`path`)
34
const isRelativeUrl = require(`is-relative-url`)
45
const _ = require(`lodash`)
@@ -29,15 +30,34 @@ module.exports = (
2930

3031
const options = _.defaults(pluginOptions, defaults)
3132

33+
const findParentLinks = ({ children }) =>
34+
children.some(
35+
node =>
36+
(node.type === `html` && !!node.value.match(/<a /)) ||
37+
node.type === `link`
38+
)
39+
40+
// This will allow the use of html image tags
41+
// const rawHtmlNodes = select(markdownAST, `html`)
42+
let rawHtmlNodes = []
43+
visitWithParents(markdownAST, `html`, (node, ancestors) => {
44+
const inLink = ancestors.some(findParentLinks)
45+
46+
rawHtmlNodes.push({ node, inLink })
47+
})
48+
3249
// This will only work for markdown syntax image tags
33-
const markdownImageNodes = select(markdownAST, `image`)
50+
let markdownImageNodes = []
51+
52+
visitWithParents(markdownAST, `image`, (node, ancestors) => {
53+
const inLink = ancestors.some(findParentLinks)
3454

35-
// This will also allow the use of html image tags
36-
const rawHtmlNodes = select(markdownAST, `html`)
55+
markdownImageNodes.push({ node, inLink })
56+
})
3757

3858
// Takes a node and generates the needed images and then returns
3959
// the needed HTML replacement for the image
40-
const generateImagesAndUpdateNode = async function(node, resolve) {
60+
const generateImagesAndUpdateNode = async function(node, resolve, inLink) {
4161
// Check if this markdownNode has a File parent. This plugin
4262
// won't work if the image isn't hosted locally.
4363
const parentNode = getNode(markdownNode.parent)
@@ -138,7 +158,7 @@ module.exports = (
138158
<img
139159
class="${imageClass}"
140160
style="${imageStyle}"
141-
src="${fallbackSrc}"
161+
src="${fallbackSrc}"
142162
alt="${node.alt ? node.alt : defaultAlt}"
143163
title="${node.title ? node.title : ``}"
144164
src="${fallbackSrc}"
@@ -165,7 +185,7 @@ module.exports = (
165185
`
166186

167187
// Make linking to original image optional.
168-
if (options.linkImagesToOriginal) {
188+
if (!inLink && options.linkImagesToOriginal) {
169189
rawHTML = `
170190
<a
171191
class="gatsby-resp-image-link"
@@ -196,7 +216,7 @@ module.exports = (
196216
return Promise.all(
197217
// Simple because there is no nesting in markdown
198218
markdownImageNodes.map(
199-
node =>
219+
({ node, inLink }) =>
200220
new Promise(async (resolve, reject) => {
201221
const fileType = node.url.slice(-3)
202222

@@ -207,7 +227,11 @@ module.exports = (
207227
fileType !== `gif` &&
208228
fileType !== `svg`
209229
) {
210-
const rawHTML = await generateImagesAndUpdateNode(node, resolve)
230+
const rawHTML = await generateImagesAndUpdateNode(
231+
node,
232+
resolve,
233+
inLink
234+
)
211235

212236
if (rawHTML) {
213237
// Replace the image node with an inline HTML node.
@@ -226,7 +250,7 @@ module.exports = (
226250
Promise.all(
227251
// Complex because HTML nodes can contain multiple images
228252
rawHtmlNodes.map(
229-
node =>
253+
({ node, inLink }) =>
230254
new Promise(async (resolve, reject) => {
231255
if (!node.value) {
232256
return resolve()
@@ -265,7 +289,8 @@ module.exports = (
265289
) {
266290
const rawHTML = await generateImagesAndUpdateNode(
267291
formattedImgTag,
268-
resolve
292+
resolve,
293+
inLink
269294
)
270295

271296
if (rawHTML) {

0 commit comments

Comments
 (0)