Skip to content

Commit

Permalink
feat(gatsby-remark-copy-linked-files): Add absolutePath to dir func…
Browse files Browse the repository at this point in the history
…tion (#36213)

Co-authored-by: Karl Horky <karl.horky@gmail.com>
Co-authored-by: Lennart <lekoarts@gmail.com>
  • Loading branch information
karlhorky and LekoArts authored Sep 14, 2022
1 parent 7ef4a3f commit f141c59
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 108 deletions.
187 changes: 90 additions & 97 deletions packages/gatsby-remark-copy-linked-files/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# gatsby-remark-copy-linked-files

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

**A sample markdown file:**

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

**When you build your site:**

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.
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.

> **Note**: The `my-awesome-pdf.pdf` file should be in the same directory as the markdown file.
---

## Install plugin
## Installation

`npm install gatsby-remark-copy-linked-files`
```shell
npm install gatsby-remark-copy-linked-files
```

## Add plugin to Gatsby Config
## Configuration

**Default settings:**
### Default settings

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

```javascript
// In your gatsby-config.js

// add plugin by name only
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [`gatsby-remark-copy-linked-files`],
```js:title=gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [`gatsby-remark-copy-linked-files`],
},
},
},
]
],
}
```

**Custom settings:**

```js
// In your gatsby-config.js

// add plugin by name and options
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-copy-linked-files`,
options: {
destinationDir: `path/to/dir`,
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
### Custom settings

```js:title=gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-copy-linked-files`,
options: {
destinationDir: `path/to/dir`,
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
},
},
},
],
],
},
},
},
]
],
}
```

---

## Custom set where to copy the files using `destinationDir`
## Option: `destinationDir`

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

> 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`
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`

### Simple usage

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

```js
// In your gatsby-config.js
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: "gatsby-remark-copy-linked-files",
options: {
destinationDir: "path/to/dir",
},
```js:title=gatsby-config.js
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: "gatsby-remark-copy-linked-files",
options: {
destinationDir: "path/to/dir",
},
],
},
},
],
},
]
}
```

> 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`
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`

### Advanced usage

For more advanced control, set `destinationDir` to a function expression using properties `name` and/or `hash` to specify the path.
For more control, set `destinationDir` to a function expression using properties `name`, `hash`, and `absolutePath` to specify the path.

- `name`: The name of the file without the file extension
- `hash`: The `internal.contentDigest` on the `File` node (guarantees a unique identifier)
- `absolutePath`: The absolute path to the file, e.g. `/Users/your-name/example/project/src/pages/folder/my-awesome-pdf.pdf`

**Examples:**

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

# save `my-awesome-pdf.pdf` to `public/path/to/dir/hello-my-awesome-pdf+2a0039f3a61f4510f41678438e4c863a_world.pdf`
destinationDir: f => `path/to/dir/hello-${f.name}+${f.hash}_world`

# save `src/pages/custom-folder/my-awesome-pdf.pdf` to `public/custom-folder/my-awesome-pdf.pdf`
# Note: Import `path` to use this example https://nodejs.org/api/path.html
destinationDir: f => `${path.dirname(path.relative(path.join(__dirname, `src`, `pages`), f.absolutePath))}/${f.name}`
```

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

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).

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

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

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

**Correct:**

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

```js
# cannot save outside root directory (i.e., outside `public` folder)
# cannot save outside `public` folder
destinationDir: `../path/to/dir`
destinationDir: _ => `../path/to/dir`
destinationDir: f => `../path/to/dir/${f.name}`
destinationDir: f => `../${f.hash}`
```

---

### Custom set which file types to ignore using `ignoreFileExtensions`

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 dir (i.e. `public` folder)
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.

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

```javascript
// In your gatsby-config.js
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: "gatsby-remark-copy-linked-files",
options: {
// `ignoreFileExtensions` defaults to [`png`, `jpg`, `jpeg`, `bmp`, `tiff`]
// as we assume you'll use gatsby-remark-images to handle
// images in markdown as it automatically creates responsive
// versions of images.
//
// If you'd like to not use gatsby-remark-images and just copy your
// original images to the public directory, set
// `ignoreFileExtensions` to an empty array.
ignoreFileExtensions: [],
},
```js:title=gatsby-config.js
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: "gatsby-remark-copy-linked-files",
options: {
// `ignoreFileExtensions` defaults to [`png`, `jpg`, `jpeg`, `bmp`, `tiff`]
// as we assume you'll use gatsby-remark-images to handle
// images in markdown as it automatically creates responsive
// versions of images.
//
// If you'd like to not use gatsby-remark-images and just copy your
// original images to the public directory, set
// `ignoreFileExtensions` to an empty array.
ignoreFileExtensions: [],
},
],
},
},
],
},
]
}
```

> So now, `[Download it now](image.png)` will be copied to the root dir (i.e. `public` folder)

---
So now, `[Download it now](image.png)` will be copied to the root of the `public` folder.

### Supported Markdown tags

Expand Down
23 changes: 23 additions & 0 deletions packages/gatsby-remark-copy-linked-files/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,29 @@ describe(`gatsby-remark-copy-linked-files`, () => {
})
})

it(`copies file to the destination supplied by the destinationDir function (using returned absolutePath)`, async () => {
const imgName = `sample-image`
const imgRelPath = `images/nested-dir/${imgName}.gif`
const imgPath = parentDir + imgRelPath

const markdownAST = remark.parse(`![some absolute image](${imgRelPath})`)
const customDestinationDir = f =>
`${path.dirname(f.absolutePath)}/${f.name}`
const expectedDestination = `images/nested-dir/sample-image.gif`
expect.assertions(3)
await plugin(
{ files: getFiles(imgPath), markdownAST, markdownNode, getNode },
{ destinationDir: customDestinationDir }
).then(v => {
const expectedNewPath = path.posix.join(
...[process.cwd(), `public`, expectedDestination]
)
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imgPath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(`/${expectedDestination}`)
})
})

it(`copies file to the root dir when destinationDir is not supplied`, async () => {
const markdownAST = remark.parse(
`![some absolute image](${imageRelativePath})`
Expand Down
24 changes: 13 additions & 11 deletions packages/gatsby-remark-copy-linked-files/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ const validateDestinationDir = dir => {
return true
} else if (typeof dir === `string`) {
// need to pass dummy data for validation to work
return destinationIsValid(`${dir}/h/n`)
return destinationIsValid(`${dir}/n/h/a`)
} else if (_.isFunction(dir)) {
// need to pass dummy data for validation to work
return destinationIsValid(`${dir({ name: `n`, hash: `h` })}`)
return destinationIsValid(
`${dir({ name: `n`, hash: `h`, absolutePath: `a` })}`
)
} else {
return false
}
Expand All @@ -34,14 +36,11 @@ const defaultDestination = linkNode =>

const getDestination = (linkNode, dir) => {
if (_.isFunction(dir)) {
// need to pass dummy data for validation to work
const isValidFunction = `${dir({ name: `n`, hash: `h` })}` !== `${dir({})}`
return isValidFunction
? `${dir({
name: linkNode.name,
hash: linkNode.internal.contentDigest,
})}.${linkNode.extension}`
: `${dir()}/${defaultDestination(linkNode)}`
return `${dir({
name: linkNode.name,
hash: linkNode.internal.contentDigest,
absolutePath: linkNode.absolutePath,
})}.${linkNode.extension}`
} else if (_.isString(dir)) {
return `${dir}/${defaultDestination(linkNode)}`
} else {
Expand All @@ -59,7 +58,10 @@ const newPath = (linkNode, options) => {
const newLinkURL = (linkNode, options, pathPrefix) => {
const { destinationDir } = options
const destination = getDestination(linkNode, destinationDir)
return `${pathPrefix ? pathPrefix : ``}/${destination}`
const startsWithSlash = destination.startsWith(`/`)
return `${pathPrefix ? pathPrefix : ``}${
startsWithSlash ? `` : `/`
}${destination}`
}

function toArray(buf) {
Expand Down

0 comments on commit f141c59

Please sign in to comment.