Skip to content

Commit 19eab87

Browse files
kimbaudiGatsbyJS Bot
authored and
GatsbyJS Bot
committed
feat(gatsby-remark-copy-linked-files): change default destinationDir and allow override via config (#16508)
* change default destination & make it customizable * document the new option in README
1 parent c3568b8 commit 19eab87

File tree

3 files changed

+271
-75
lines changed

3 files changed

+271
-75
lines changed

packages/gatsby-remark-copy-linked-files/README.md

+151-35
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,184 @@
11
# gatsby-remark-copy-linked-files
22

3-
Copies local files linked to/from markdown to your `public` folder.
3+
Copies local files linked to/from Markdown (`.md|.markdown`) files to the root directory (i.e., `public` folder).
44

5-
## Install
5+
**A sample markdown file:**
6+
7+
```md
8+
---
9+
title: My awesome blog post
10+
---
11+
12+
Hey everyone, I just made a sweet PDF with lots of interesting stuff in it.
13+
14+
[Download it now](my-awesome-pdf.pdf)
15+
```
16+
17+
**When you build your site:**
18+
19+
The `my-awesome-pdf.pdf` file will be copied to the root directory (i.e., `public/some-really-long-contenthash/my-awesome-pdf.pdf`) and the generated HTML page will be modified to point to it.
20+
21+
> **Note**: The `my-awesome-pdf.pdf` file should be in the same directory as the markdown file.
22+
23+
---
24+
25+
## Install plugin
626

727
`npm install --save gatsby-remark-copy-linked-files`
828

9-
## How to use
29+
## Add plugin to Gatsby Config
30+
31+
**Default settings:**
1032

11-
#### Basic usage
33+
Add `gatsby-remark-copy-linked-files` plugin as a plugin to [`gatsby-transformer-remark`](https://www.gatsbyjs.org/packages/gatsby-transformer-remark/):
1234

1335
```javascript
1436
// In your gatsby-config.js
37+
38+
// add plugin by name only
1539
plugins: [
1640
{
1741
resolve: `gatsby-transformer-remark`,
1842
options: {
19-
plugins: ["gatsby-remark-copy-linked-files"],
43+
plugins: [`gatsby-remark-copy-linked-files`],
2044
},
2145
},
2246
]
2347
```
2448

25-
### How to change the directory the files are added to.
49+
**Custom settings:**
2650

27-
By default, all files will be copied to the root of the `public` dir, but you
28-
can choose a different location using the `destinationDir` option. Provide a
29-
path, relative to the `public` directory. The path must be within the public
30-
directory, so `path/to/dir` is fine, but `../../dir` is not.
51+
```js
52+
// In your gatsby-config.js
3153

54+
// add plugin by name and options
55+
plugins: [
56+
{
57+
resolve: `gatsby-transformer-remark`,
58+
options: {
59+
plugins: [
60+
{
61+
resolve: `gatsby-remark-copy-linked-files`,
62+
options: {
63+
destinationDir: `path/to/dir`,
64+
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
65+
},
66+
},
67+
],
68+
},
69+
},
70+
]
3271
```
72+
73+
---
74+
75+
## Custom set where to copy the files using `destinationDir`
76+
77+
By default, all files will be copied to the root directory (i.e., `public` folder) in the following format: `contentHash/fileName.ext`.
78+
79+
> For example, `[Download it now](my-awesome-pdf.pdf)` will copy the file `my-awesome-pdf.pdf` to something like `public/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
80+
81+
### Simple usage
82+
83+
To change this, set `destinationDir` to a path of your own choosing (i.e., `path/to/dir`).
84+
85+
```js
3386
// In your gatsby-config.js
3487
plugins: [
3588
{
3689
resolve: `gatsby-transformer-remark`,
3790
options: {
3891
plugins: [
3992
{
40-
resolve: 'gatsby-remark-copy-linked-files',
93+
resolve: "gatsby-remark-copy-linked-files",
4194
options: {
42-
destinationDir: 'path/to/dir',
43-
}
44-
}
45-
]
46-
}
47-
}
95+
destinationDir: "path/to/dir",
96+
},
97+
},
98+
],
99+
},
100+
},
48101
]
49102
```
50103

51-
### How to override which file types are ignored
104+
> So now, `[Download it now](my-awesome-pdf.pdf)` will copy the file `my-awesome-pdf.pdf` to `public/path/to/dir/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
105+
106+
### Advanced usage
107+
108+
For more advanced control, set `destinationDir` to a function expression using properties `name` and/or `hash` to specify the path.
109+
110+
**Examples:**
111+
112+
```js
113+
# save `my-awesome-pdf.pdf` to `public/my-awesome-pdf.pdf`
114+
destinationDir: f => `${f.name}`
115+
116+
# save `my-awesome-pdf.pdf` to `public/2a0039f3a61f4510f41678438e4c863a.pdf`
117+
destinationDir: f => `${f.hash}`
118+
119+
# save `my-awesome-pdf.pdf` to `public/downloads/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
120+
destinationDir: f => `downloads/${f.hash}/${f.name}`
121+
122+
# save `my-awesome-pdf.pdf` to `public/downloads/2a0039f3a61f4510f41678438e4c863a-my-awesome-pdf.pdf`
123+
destinationDir: f => `downloads/${f.hash}-${f.name}`
124+
125+
# save `my-awesome-pdf.pdf` to `public/my-awesome-pdf/2a0039f3a61f4510f41678438e4c863a.pdf`
126+
destinationDir: f => `${f.name}/${f.hash}`
127+
128+
# save `my-awesome-pdf.pdf` to `public/path/to/dir/hello-my-awesome-pdf+2a0039f3a61f4510f41678438e4c863a_world.pdf`
129+
destinationDir: f => `path/to/dir/hello-${f.name}+${f.hash}_world`
130+
```
131+
132+
> **Note:** Make sure you use either `name` or `hash` property in your function expression!
133+
> If you don't include both `name` and `hash` properties in your function expression, `gatsby-remark-copy-linked-files` plugin will resolve the function expression to a string value and use default settings as a fallback mechanism to prevent your local files from getting copied with the same name (causing files to get overwritten).
134+
135+
```js
136+
# Note: `my-awesome-pdf.pdf` is saved to `public/hello/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
137+
# because `name` and `hash` properties are not referenced in the function expression.
138+
# So these function expressions are treated the same way
139+
destinationDir: _ => `hello`
140+
destinationDir: `hello`
141+
```
142+
143+
### Caveat: Error thrown if `destinationDir` points outside the root directory (i.e. `public` folder)
144+
145+
> **Note:** An error will be thrown if the destination points outside the root directory (i.e. `public` folder).
146+
147+
**Correct:**
148+
149+
```js
150+
# saves to `public/path/to/dir/`
151+
destinationDir: `path/to/dir`
152+
153+
# saves to `public/path/to/dir/`
154+
destinationDir: _ => `path/to/dir`
155+
156+
# saves to `public/path/to/dir/fileName.ext`
157+
destinationDir: f => `path/to/dir/${f.name}`
158+
159+
# saves to `public/contentHash.ext`
160+
destinationDir: f => `${f.hash}`
161+
```
162+
163+
**Error thrown:**
164+
165+
```js
166+
# cannot save outside root directory (i.e., outside `public` folder)
167+
destinationDir: `../path/to/dir`
168+
destinationDir: _ => `../path/to/dir`
169+
destinationDir: f => `../path/to/dir/${f.name}`
170+
destinationDir: f => `../${f.hash}`
171+
```
172+
173+
---
174+
175+
### Custom set which file types to ignore using `ignoreFileExtensions`
176+
177+
By default, the file types that this plugin ignores are: `png`, `jpg`, `jpeg`, `bmp`, `tiff`.
178+
179+
> For example, `[Download it now](image.png)` will be ignored and not copied to the root dir (i.e. `public` folder)
180+
181+
To change this, set `ignoreFileExtensions` to an array of extensions to ignore (i.e., an empty array `[]` to ignore nothing).
52182

53183
```javascript
54184
// In your gatsby-config.js
@@ -77,28 +207,14 @@ plugins: [
77207
]
78208
```
79209

80-
Then in your Markdown files, link to the file you desire to reference.
81-
82-
E.g.
210+
> So now, `[Download it now](image.png)` will be copied to the root dir (i.e. `public` folder)
83211

84-
```markdown
85212
---
86-
title: My awesome blog post
87-
---
88-
89-
Hey everyone, I just made a sweet PDF with lots of interesting stuff in it.
90-
91-
[Download it now](my-awesome-pdf.pdf)
92-
```
93-
94-
`my-awesome-pdf.pdf` should be in the same directory as the markdown file. When
95-
you build your site, the file will be copied to the `public` folder and the
96-
markdown HTML will be modified to point to it.
97213

98214
### Supported Markdown tags
99215

100-
- img
101-
- link
216+
- img - `![Image](my-img.png)`
217+
- link - `[Link](myFile.txt)`
102218

103219
### Supported HTML tags
104220

packages/gatsby-remark-copy-linked-files/src/__tests__/index.js

+72-10
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ describe(`gatsby-remark-copy-linked-files`, () => {
265265
describe(`options.destinationDir`, () => {
266266
const imagePath = `images/sample-image.gif`
267267

268-
it(`throws an error if the destination directory is not within 'public'`, async () => {
268+
it(`throws an error if the destination supplied by destinationDir points outside of the root dir`, async () => {
269269
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
270270
const invalidDestinationDir = `../destination`
271271
expect.assertions(2)
@@ -280,14 +280,31 @@ describe(`gatsby-remark-copy-linked-files`, () => {
280280
})
281281
})
282282

283-
it(`copies file to destinationDir when supplied`, async () => {
283+
it(`throws an error if the destination supplied by the destinationDir function points outside of the root dir`, async () => {
284+
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
285+
const invalidDestinationDir = `../destination`
286+
const customDestinationDir = f =>
287+
`../destination/${f.hash}/${f.name}/${f.notexist}`
288+
expect.assertions(2)
289+
return plugin(
290+
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
291+
{
292+
destinationDir: customDestinationDir,
293+
}
294+
).catch(e => {
295+
expect(e).toEqual(expect.stringContaining(invalidDestinationDir))
296+
expect(fsExtra.copy).not.toHaveBeenCalled()
297+
})
298+
})
299+
300+
it(`copies file to the destination supplied by destinationDir`, async () => {
284301
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
285302
const validDestinationDir = `path/to/dir`
286303
const expectedNewPath = path.posix.join(
287304
process.cwd(),
288305
`public`,
289306
validDestinationDir,
290-
`/undefined-undefined.gif`
307+
`/undefined/undefined.gif`
291308
)
292309
expect.assertions(3)
293310
await plugin(
@@ -299,20 +316,38 @@ describe(`gatsby-remark-copy-linked-files`, () => {
299316
expect(v).toBeDefined()
300317
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
301318
expect(imageURL(markdownAST)).toEqual(
302-
`/path/to/dir/undefined-undefined.gif`
319+
`/path/to/dir/undefined/undefined.gif`
320+
)
321+
})
322+
})
323+
324+
it(`copies file to the destination supplied by the destinationDir function`, async () => {
325+
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
326+
const customDestinationDir = f => `foo/${f.hash}--bar`
327+
const expectedDestination = `foo/undefined--bar.gif`
328+
expect.assertions(3)
329+
await plugin(
330+
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
331+
{ destinationDir: customDestinationDir }
332+
).then(v => {
333+
const expectedNewPath = path.posix.join(
334+
...[process.cwd(), `public`, expectedDestination]
303335
)
336+
expect(v).toBeDefined()
337+
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
338+
expect(imageURL(markdownAST)).toEqual(`/${expectedDestination}`)
304339
})
305340
})
306341

307-
it(`copies file to destinationDir when supplied (with pathPrefix)`, async () => {
342+
it(`copies file to the destination supplied by destinationDir (with pathPrefix)`, async () => {
308343
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
309344
const pathPrefix = `/blog`
310345
const validDestinationDir = `path/to/dir`
311346
const expectedNewPath = path.posix.join(
312347
process.cwd(),
313348
`public`,
314349
validDestinationDir,
315-
`/undefined-undefined.gif`
350+
`/undefined/undefined.gif`
316351
)
317352
expect.assertions(3)
318353
await plugin(
@@ -330,17 +365,44 @@ describe(`gatsby-remark-copy-linked-files`, () => {
330365
expect(v).toBeDefined()
331366
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
332367
expect(imageURL(markdownAST)).toEqual(
333-
`${pathPrefix}/path/to/dir/undefined-undefined.gif`
368+
`${pathPrefix}/path/to/dir/undefined/undefined.gif`
369+
)
370+
})
371+
})
372+
373+
it(`copies file to the destination supplied by the destinationDir function (with pathPrefix)`, async () => {
374+
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
375+
const pathPrefix = `/blog`
376+
const customDestinationDir = f => `hello${f.name}123`
377+
const expectedDestination = `helloundefined123.gif`
378+
expect.assertions(3)
379+
await plugin(
380+
{
381+
files: getFiles(imagePath),
382+
markdownAST,
383+
markdownNode,
384+
pathPrefix,
385+
getNode,
386+
},
387+
{ destinationDir: customDestinationDir }
388+
).then(v => {
389+
const expectedNewPath = path.posix.join(
390+
...[process.cwd(), `public`, expectedDestination]
391+
)
392+
expect(v).toBeDefined()
393+
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
394+
expect(imageURL(markdownAST)).toEqual(
395+
`${pathPrefix}/${expectedDestination}`
334396
)
335397
})
336398
})
337399

338-
it(`copies file to root dir when not supplied'`, async () => {
400+
it(`copies file to the root dir when destinationDir is not supplied'`, async () => {
339401
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
340402
const expectedNewPath = path.posix.join(
341403
process.cwd(),
342404
`public`,
343-
`/undefined-undefined.gif`
405+
`/undefined/undefined.gif`
344406
)
345407
expect.assertions(3)
346408
await plugin({
@@ -351,7 +413,7 @@ describe(`gatsby-remark-copy-linked-files`, () => {
351413
}).then(v => {
352414
expect(v).toBeDefined()
353415
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
354-
expect(imageURL(markdownAST)).toEqual(`/undefined-undefined.gif`)
416+
expect(imageURL(markdownAST)).toEqual(`/undefined/undefined.gif`)
355417
})
356418
})
357419
})

0 commit comments

Comments
 (0)