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): Add absolutePath to dir function #36213

Merged
merged 7 commits into from
Sep 14, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
188 changes: 91 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`.
LekoArts marked this conversation as resolved.
Show resolved Hide resolved

> 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 advanced control, set `destinationDir` to a function expression using properties `name`, `hash`, and `absolutePath` to specify the path.
LekoArts marked this conversation as resolved.
Show resolved Hide resolved

- `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,16 @@ 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`

# Given the file `my-awesome-pdf.pdf` at `src/pages/custom-folder`
# save `my-awesome-pdf.pdf` to `public/custom-folder/my-awesome-pdf.pdf`
LekoArts marked this conversation as resolved.
Show resolved Hide resolved
# Note: You have to import `path`
LekoArts marked this conversation as resolved.
Show resolved Hide resolved
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 +143,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 +166,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.
LekoArts marked this conversation as resolved.
Show resolved Hide resolved

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.
LekoArts marked this conversation as resolved.
Show resolved Hide resolved

### 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 () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could also rework all the messages in the tests to remove "root" and refer to "public" instead...

but we could also just leave it! 🤷‍♂️

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