Skip to content

Commit f141c59

Browse files
karlhorkyLekoArts
andauthored
feat(gatsby-remark-copy-linked-files): Add absolutePath to dir function (#36213)
Co-authored-by: Karl Horky <[email protected]> Co-authored-by: Lennart <[email protected]>
1 parent 7ef4a3f commit f141c59

File tree

3 files changed

+126
-108
lines changed

3 files changed

+126
-108
lines changed

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

+90-97
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# gatsby-remark-copy-linked-files
22

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

55
**A sample markdown file:**
66

@@ -16,96 +16,93 @@ Hey everyone, I just made a sweet PDF with lots of interesting stuff in it.
1616

1717
**When you build your site:**
1818

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.
19+
The `my-awesome-pdf.pdf` file will be copied to the `public` folder (i.e., `public/some-really-long-contenthash/my-awesome-pdf.pdf`) and the generated HTML page will be modified to point to it.
2020

2121
> **Note**: The `my-awesome-pdf.pdf` file should be in the same directory as the markdown file.
2222
23-
---
24-
25-
## Install plugin
23+
## Installation
2624

27-
`npm install gatsby-remark-copy-linked-files`
25+
```shell
26+
npm install gatsby-remark-copy-linked-files
27+
```
2828

29-
## Add plugin to Gatsby Config
29+
## Configuration
3030

31-
**Default settings:**
31+
### Default settings
3232

3333
Add `gatsby-remark-copy-linked-files` plugin as a plugin to [`gatsby-transformer-remark`](https://www.gatsbyjs.com/plugins/gatsby-transformer-remark/):
3434

35-
```javascript
36-
// In your gatsby-config.js
37-
38-
// add plugin by name only
39-
plugins: [
40-
{
41-
resolve: `gatsby-transformer-remark`,
42-
options: {
43-
plugins: [`gatsby-remark-copy-linked-files`],
35+
```js:title=gatsby-config.js
36+
module.exports = {
37+
plugins: [
38+
{
39+
resolve: `gatsby-transformer-remark`,
40+
options: {
41+
plugins: [`gatsby-remark-copy-linked-files`],
42+
},
4443
},
45-
},
46-
]
44+
],
45+
}
4746
```
4847

49-
**Custom settings:**
50-
51-
```js
52-
// In your gatsby-config.js
53-
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`],
48+
### Custom settings
49+
50+
```js:title=gatsby-config.js
51+
module.exports = {
52+
plugins: [
53+
{
54+
resolve: `gatsby-transformer-remark`,
55+
options: {
56+
plugins: [
57+
{
58+
resolve: `gatsby-remark-copy-linked-files`,
59+
options: {
60+
destinationDir: `path/to/dir`,
61+
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
62+
},
6563
},
66-
},
67-
],
64+
],
65+
},
6866
},
69-
},
70-
]
67+
],
68+
}
7169
```
7270

73-
---
74-
75-
## Custom set where to copy the files using `destinationDir`
71+
## Option: `destinationDir`
7672

77-
By default, all files will be copied to the root directory (i.e., `public` folder) in the following format: `contentHash/fileName.ext`.
73+
By default, all files will be copied to the root of the `public` folder in the following format: `contentHash/fileName.ext`.
7874

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`
75+
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`
8076

8177
### Simple usage
8278

8379
To change this, set `destinationDir` to a path of your own choosing (i.e., `path/to/dir`).
8480

85-
```js
86-
// In your gatsby-config.js
87-
plugins: [
88-
{
89-
resolve: `gatsby-transformer-remark`,
90-
options: {
91-
plugins: [
92-
{
93-
resolve: "gatsby-remark-copy-linked-files",
94-
options: {
95-
destinationDir: "path/to/dir",
96-
},
81+
```js:title=gatsby-config.js
82+
{
83+
resolve: `gatsby-transformer-remark`,
84+
options: {
85+
plugins: [
86+
{
87+
resolve: "gatsby-remark-copy-linked-files",
88+
options: {
89+
destinationDir: "path/to/dir",
9790
},
98-
],
99-
},
91+
},
92+
],
10093
},
101-
]
94+
}
10295
```
10396

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`
97+
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`
10598

10699
### Advanced usage
107100

108-
For more advanced control, set `destinationDir` to a function expression using properties `name` and/or `hash` to specify the path.
101+
For more control, set `destinationDir` to a function expression using properties `name`, `hash`, and `absolutePath` to specify the path.
102+
103+
- `name`: The name of the file without the file extension
104+
- `hash`: The `internal.contentDigest` on the `File` node (guarantees a unique identifier)
105+
- `absolutePath`: The absolute path to the file, e.g. `/Users/your-name/example/project/src/pages/folder/my-awesome-pdf.pdf`
109106

110107
**Examples:**
111108

@@ -127,10 +124,15 @@ destinationDir: f => `${f.name}/${f.hash}`
127124

128125
# save `my-awesome-pdf.pdf` to `public/path/to/dir/hello-my-awesome-pdf+2a0039f3a61f4510f41678438e4c863a_world.pdf`
129126
destinationDir: f => `path/to/dir/hello-${f.name}+${f.hash}_world`
127+
128+
# save `src/pages/custom-folder/my-awesome-pdf.pdf` to `public/custom-folder/my-awesome-pdf.pdf`
129+
# Note: Import `path` to use this example https://nodejs.org/api/path.html
130+
destinationDir: f => `${path.dirname(path.relative(path.join(__dirname, `src`, `pages`), f.absolutePath))}/${f.name}`
130131
```
131132

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).
133+
**Please note:** Make sure you use either `name` or `hash` property in your function expression!
134+
135+
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).
134136

135137
```js
136138
# Note: `my-awesome-pdf.pdf` is saved to `public/hello/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
@@ -140,9 +142,9 @@ destinationDir: _ => `hello`
140142
destinationDir: `hello`
141143
```
142144

143-
### Caveat: Error thrown if `destinationDir` points outside the root directory (i.e. `public` folder)
145+
### Caveat: Error thrown if `destinationDir` points outside the `public` folder
144146

145-
> **Note:** An error will be thrown if the destination points outside the root directory (i.e. `public` folder).
147+
**Please note:** An error will be thrown if the destination points outside the `public` folder.
146148

147149
**Correct:**
148150

@@ -163,53 +165,44 @@ destinationDir: f => `${f.hash}`
163165
**Error thrown:**
164166

165167
```js
166-
# cannot save outside root directory (i.e., outside `public` folder)
168+
# cannot save outside `public` folder
167169
destinationDir: `../path/to/dir`
168170
destinationDir: _ => `../path/to/dir`
169171
destinationDir: f => `../path/to/dir/${f.name}`
170172
destinationDir: f => `../${f.hash}`
171173
```
172174

173-
---
174-
175175
### Custom set which file types to ignore using `ignoreFileExtensions`
176176

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)
177+
By default, the file types that this plugin ignores are: `png`, `jpg`, `jpeg`, `bmp`, `tiff`. For example, `[Download it now](image.png)` will be ignored and not copied to the root of the `public` folder.
180178

181179
To change this, set `ignoreFileExtensions` to an array of extensions to ignore (i.e., an empty array `[]` to ignore nothing).
182180

183-
```javascript
184-
// In your gatsby-config.js
185-
plugins: [
186-
{
187-
resolve: `gatsby-transformer-remark`,
188-
options: {
189-
plugins: [
190-
{
191-
resolve: "gatsby-remark-copy-linked-files",
192-
options: {
193-
// `ignoreFileExtensions` defaults to [`png`, `jpg`, `jpeg`, `bmp`, `tiff`]
194-
// as we assume you'll use gatsby-remark-images to handle
195-
// images in markdown as it automatically creates responsive
196-
// versions of images.
197-
//
198-
// If you'd like to not use gatsby-remark-images and just copy your
199-
// original images to the public directory, set
200-
// `ignoreFileExtensions` to an empty array.
201-
ignoreFileExtensions: [],
202-
},
181+
```js:title=gatsby-config.js
182+
{
183+
resolve: `gatsby-transformer-remark`,
184+
options: {
185+
plugins: [
186+
{
187+
resolve: "gatsby-remark-copy-linked-files",
188+
options: {
189+
// `ignoreFileExtensions` defaults to [`png`, `jpg`, `jpeg`, `bmp`, `tiff`]
190+
// as we assume you'll use gatsby-remark-images to handle
191+
// images in markdown as it automatically creates responsive
192+
// versions of images.
193+
//
194+
// If you'd like to not use gatsby-remark-images and just copy your
195+
// original images to the public directory, set
196+
// `ignoreFileExtensions` to an empty array.
197+
ignoreFileExtensions: [],
203198
},
204-
],
205-
},
199+
},
200+
],
206201
},
207-
]
202+
}
208203
```
209204

210-
> So now, `[Download it now](image.png)` will be copied to the root dir (i.e. `public` folder)
211-
212-
---
205+
So now, `[Download it now](image.png)` will be copied to the root of the `public` folder.
213206

214207
### Supported Markdown tags
215208

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

+23
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,29 @@ describe(`gatsby-remark-copy-linked-files`, () => {
526526
})
527527
})
528528

529+
it(`copies file to the destination supplied by the destinationDir function (using returned absolutePath)`, async () => {
530+
const imgName = `sample-image`
531+
const imgRelPath = `images/nested-dir/${imgName}.gif`
532+
const imgPath = parentDir + imgRelPath
533+
534+
const markdownAST = remark.parse(`![some absolute image](${imgRelPath})`)
535+
const customDestinationDir = f =>
536+
`${path.dirname(f.absolutePath)}/${f.name}`
537+
const expectedDestination = `images/nested-dir/sample-image.gif`
538+
expect.assertions(3)
539+
await plugin(
540+
{ files: getFiles(imgPath), markdownAST, markdownNode, getNode },
541+
{ destinationDir: customDestinationDir }
542+
).then(v => {
543+
const expectedNewPath = path.posix.join(
544+
...[process.cwd(), `public`, expectedDestination]
545+
)
546+
expect(v).toBeDefined()
547+
expect(fsExtra.copy).toHaveBeenCalledWith(imgPath, expectedNewPath)
548+
expect(imageURL(markdownAST)).toEqual(`/${expectedDestination}`)
549+
})
550+
})
551+
529552
it(`copies file to the root dir when destinationDir is not supplied`, async () => {
530553
const markdownAST = remark.parse(
531554
`![some absolute image](${imageRelativePath})`

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

+13-11
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ const validateDestinationDir = dir => {
2020
return true
2121
} else if (typeof dir === `string`) {
2222
// need to pass dummy data for validation to work
23-
return destinationIsValid(`${dir}/h/n`)
23+
return destinationIsValid(`${dir}/n/h/a`)
2424
} else if (_.isFunction(dir)) {
2525
// need to pass dummy data for validation to work
26-
return destinationIsValid(`${dir({ name: `n`, hash: `h` })}`)
26+
return destinationIsValid(
27+
`${dir({ name: `n`, hash: `h`, absolutePath: `a` })}`
28+
)
2729
} else {
2830
return false
2931
}
@@ -34,14 +36,11 @@ const defaultDestination = linkNode =>
3436

3537
const getDestination = (linkNode, dir) => {
3638
if (_.isFunction(dir)) {
37-
// need to pass dummy data for validation to work
38-
const isValidFunction = `${dir({ name: `n`, hash: `h` })}` !== `${dir({})}`
39-
return isValidFunction
40-
? `${dir({
41-
name: linkNode.name,
42-
hash: linkNode.internal.contentDigest,
43-
})}.${linkNode.extension}`
44-
: `${dir()}/${defaultDestination(linkNode)}`
39+
return `${dir({
40+
name: linkNode.name,
41+
hash: linkNode.internal.contentDigest,
42+
absolutePath: linkNode.absolutePath,
43+
})}.${linkNode.extension}`
4544
} else if (_.isString(dir)) {
4645
return `${dir}/${defaultDestination(linkNode)}`
4746
} else {
@@ -59,7 +58,10 @@ const newPath = (linkNode, options) => {
5958
const newLinkURL = (linkNode, options, pathPrefix) => {
6059
const { destinationDir } = options
6160
const destination = getDestination(linkNode, destinationDir)
62-
return `${pathPrefix ? pathPrefix : ``}/${destination}`
61+
const startsWithSlash = destination.startsWith(`/`)
62+
return `${pathPrefix ? pathPrefix : ``}${
63+
startsWithSlash ? `` : `/`
64+
}${destination}`
6365
}
6466

6567
function toArray(buf) {

0 commit comments

Comments
 (0)