Skip to content

Commit

Permalink
feat(gatsby-remark-copy-linked-files): change default destinationDir …
Browse files Browse the repository at this point in the history
…and allow override via config (#16508)

* change default destination & make it customizable

* document the new option in README
  • Loading branch information
kimbaudi authored and GatsbyJS Bot committed Aug 10, 2019
1 parent c3568b8 commit 19eab87
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 75 deletions.
186 changes: 151 additions & 35 deletions packages/gatsby-remark-copy-linked-files/README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,184 @@
# gatsby-remark-copy-linked-files

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

## Install
**A sample markdown file:**

```md
---
title: My awesome blog post
---

Hey everyone, I just made a sweet PDF with lots of interesting stuff in it.

[Download it now](my-awesome-pdf.pdf)
```

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

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

## Install plugin

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

## How to use
## Add plugin to Gatsby Config

**Default settings:**

#### Basic usage
Add `gatsby-remark-copy-linked-files` plugin as a plugin to [`gatsby-transformer-remark`](https://www.gatsbyjs.org/packages/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"],
plugins: [`gatsby-remark-copy-linked-files`],
},
},
]
```

### How to change the directory the files are added to.
**Custom settings:**

By default, all files will be copied to the root of the `public` dir, but you
can choose a different location using the `destinationDir` option. Provide a
path, relative to the `public` directory. The path must be within the public
directory, so `path/to/dir` is fine, but `../../dir` is not.
```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 set where to copy the files using `destinationDir`

By default, all files will be copied to the root directory (i.e., `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`
### 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',
resolve: "gatsby-remark-copy-linked-files",
options: {
destinationDir: 'path/to/dir',
}
}
]
}
}
destinationDir: "path/to/dir",
},
},
],
},
},
]
```

### How to override which file types are ignored
> 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.

**Examples:**

```js
# save `my-awesome-pdf.pdf` to `public/my-awesome-pdf.pdf`
destinationDir: f => `${f.name}`

# save `my-awesome-pdf.pdf` to `public/2a0039f3a61f4510f41678438e4c863a.pdf`
destinationDir: f => `${f.hash}`

# save `my-awesome-pdf.pdf` to `public/downloads/2a0039f3a61f4510f41678438e4c863a/my-awesome-pdf.pdf`
destinationDir: f => `downloads/${f.hash}/${f.name}`

# save `my-awesome-pdf.pdf` to `public/downloads/2a0039f3a61f4510f41678438e4c863a-my-awesome-pdf.pdf`
destinationDir: f => `downloads/${f.hash}-${f.name}`

# save `my-awesome-pdf.pdf` to `public/my-awesome-pdf/2a0039f3a61f4510f41678438e4c863a.pdf`
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`
```

> **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`
# because `name` and `hash` properties are not referenced in the function expression.
# So these function expressions are treated the same way
destinationDir: _ => `hello`
destinationDir: `hello`
```

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

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

**Correct:**

```js
# saves to `public/path/to/dir/`
destinationDir: `path/to/dir`

# saves to `public/path/to/dir/`
destinationDir: _ => `path/to/dir`

# saves to `public/path/to/dir/fileName.ext`
destinationDir: f => `path/to/dir/${f.name}`
# saves to `public/contentHash.ext`
destinationDir: f => `${f.hash}`
```

**Error thrown:**

```js
# cannot save outside root directory (i.e., 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)

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
Expand Down Expand Up @@ -77,28 +207,14 @@ plugins: [
]
```

Then in your Markdown files, link to the file you desire to reference.

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

```markdown
---
title: My awesome blog post
---

Hey everyone, I just made a sweet PDF with lots of interesting stuff in it.

[Download it now](my-awesome-pdf.pdf)
```

`my-awesome-pdf.pdf` should be in the same directory as the markdown file. When
you build your site, the file will be copied to the `public` folder and the
markdown HTML will be modified to point to it.

### Supported Markdown tags

- img
- link
- img - `![Image](my-img.png)`
- link - `[Link](myFile.txt)`

### Supported HTML tags

Expand Down
82 changes: 72 additions & 10 deletions packages/gatsby-remark-copy-linked-files/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ describe(`gatsby-remark-copy-linked-files`, () => {
describe(`options.destinationDir`, () => {
const imagePath = `images/sample-image.gif`

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

it(`copies file to destinationDir when supplied`, async () => {
it(`throws an error if the destination supplied by the destinationDir function points outside of the root dir`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const invalidDestinationDir = `../destination`
const customDestinationDir = f =>
`../destination/${f.hash}/${f.name}/${f.notexist}`
expect.assertions(2)
return plugin(
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
{
destinationDir: customDestinationDir,
}
).catch(e => {
expect(e).toEqual(expect.stringContaining(invalidDestinationDir))
expect(fsExtra.copy).not.toHaveBeenCalled()
})
})

it(`copies file to the destination supplied by destinationDir`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const validDestinationDir = `path/to/dir`
const expectedNewPath = path.posix.join(
process.cwd(),
`public`,
validDestinationDir,
`/undefined-undefined.gif`
`/undefined/undefined.gif`
)
expect.assertions(3)
await plugin(
Expand All @@ -299,20 +316,38 @@ describe(`gatsby-remark-copy-linked-files`, () => {
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(
`/path/to/dir/undefined-undefined.gif`
`/path/to/dir/undefined/undefined.gif`
)
})
})

it(`copies file to the destination supplied by the destinationDir function`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const customDestinationDir = f => `foo/${f.hash}--bar`
const expectedDestination = `foo/undefined--bar.gif`
expect.assertions(3)
await plugin(
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
{ destinationDir: customDestinationDir }
).then(v => {
const expectedNewPath = path.posix.join(
...[process.cwd(), `public`, expectedDestination]
)
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(`/${expectedDestination}`)
})
})

it(`copies file to destinationDir when supplied (with pathPrefix)`, async () => {
it(`copies file to the destination supplied by destinationDir (with pathPrefix)`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const pathPrefix = `/blog`
const validDestinationDir = `path/to/dir`
const expectedNewPath = path.posix.join(
process.cwd(),
`public`,
validDestinationDir,
`/undefined-undefined.gif`
`/undefined/undefined.gif`
)
expect.assertions(3)
await plugin(
Expand All @@ -330,17 +365,44 @@ describe(`gatsby-remark-copy-linked-files`, () => {
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(
`${pathPrefix}/path/to/dir/undefined-undefined.gif`
`${pathPrefix}/path/to/dir/undefined/undefined.gif`
)
})
})

it(`copies file to the destination supplied by the destinationDir function (with pathPrefix)`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const pathPrefix = `/blog`
const customDestinationDir = f => `hello${f.name}123`
const expectedDestination = `helloundefined123.gif`
expect.assertions(3)
await plugin(
{
files: getFiles(imagePath),
markdownAST,
markdownNode,
pathPrefix,
getNode,
},
{ destinationDir: customDestinationDir }
).then(v => {
const expectedNewPath = path.posix.join(
...[process.cwd(), `public`, expectedDestination]
)
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(
`${pathPrefix}/${expectedDestination}`
)
})
})

it(`copies file to root dir when not supplied'`, async () => {
it(`copies file to the root dir when destinationDir is not supplied'`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const expectedNewPath = path.posix.join(
process.cwd(),
`public`,
`/undefined-undefined.gif`
`/undefined/undefined.gif`
)
expect.assertions(3)
await plugin({
Expand All @@ -351,7 +413,7 @@ describe(`gatsby-remark-copy-linked-files`, () => {
}).then(v => {
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(`/undefined-undefined.gif`)
expect(imageURL(markdownAST)).toEqual(`/undefined/undefined.gif`)
})
})
})
Expand Down
Loading

0 comments on commit 19eab87

Please sign in to comment.