Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gatsby-remark-copy-linked-files): change default destinationDir and allow override via config #16508

Merged
merged 2 commits into from
Aug 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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