Skip to content

Commit 28fddbc

Browse files
authored
Merge pull request #5 from dansanderson/master
"copyfiles" block allows documents to request files to copy
2 parents 3a2ee74 + 1f564b1 commit 28fddbc

File tree

4 files changed

+119
-34
lines changed

4 files changed

+119
-34
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# ChangeLog
22

3+
## v1.0.3
4+
5+
### Features
6+
7+
- A new `copyfiles` code fence allows a Markdown file to declare files to be copied without linking to them directly. This is useful when linking to an HTML file that itself links to additional files that must be copied.
8+
39
## v1.0.2
410

511
### Fixes

README.md

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
Copies local files relative linked to/from markdown to your `public` folder with preserve directory structure.
99

10-
This will copy the files linked relative to all Markdowns like [gatsby-remark-copy-linked-files](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-copy-linked-files) into a public directory structure like [gatsby-remark-copy-images](https://github.com/mojodna/gatsby-remark-copy-images) as it is.
10+
This will copy the files linked relative to all Markdowns like [gatsby-remark-copy-linked-files](https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-remark-copy-linked-files) into a public directory structure like [gatsby-remark-copy-images](https://github.com/mojodna/gatsby-remark-copy-images) as it is. It can also copy additional files requested by the document.
1111

1212
## Install
1313

@@ -29,27 +29,37 @@ plugins: [
2929
]
3030
```
3131

32+
*Note:* When using the `copyfiles` code fence (see below), `gatsby-remark-copy-relative-linked-files` must appear before general purpose code fence processors like `gatsby-remark-prismjs`.
33+
3234
Then in your Markdown files, simply reference the files.
3335

3436
### E.g.
3537

3638
```markdown
37-
---
38-
title: My awesome blog post
39-
---
40-
41-
Hey everyone, I just made a sweet files with lots of interesting stuff in
42-
it.
43-
44-
- ![](image.gif)
45-
- [archive.zip](archive.zip)
46-
- [sample.pdf](sample.pdf)
47-
- [not-copy.rar](https://example.com/not-copy.rar)
39+
---
40+
title: My awesome blog post
41+
---
42+
43+
Hey everyone, I just made a sweet files with lots of interesting stuff in
44+
it.
45+
46+
- ![](image.gif)
47+
- [archive.zip](archive.zip)
48+
- [sample.pdf](sample.pdf)
49+
- [report.html](report.html)
50+
- [not-copy.rar](https://example.com/not-copy.rar)
51+
52+
```copyfiles
53+
report.css
54+
diagram.png
55+
```
4856
```
4957

50-
`image.gif`, `archive.zip` and `sample.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.
58+
`image.gif`, `archive.zip`, `sample.pdf` and `report.html` 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.
59+
60+
Similarly, `report.css` and `diagram.png` should be in the same directory as the Markdown file. In this example, `report.html` has its own internal relative links to these files. `report.html` is not changed in any way. The relative links to the copied files work from the copied location.
5161

52-
The copy target is a relative link. Therefore, links starting with `XXXX://` or `//` are ignore. In this example `not-copy.rar` is not copied.
62+
The copy target is a relative link. Therefore, links starting with `XXXX://` or `//` are ignored. In this example `not-copy.rar` is not copied.
5363

5464
## Options
5565

src/index.js

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,16 @@ module.exports = (
4747
{ files, linkPrefix, markdownNode, markdownAST, getNode },
4848
pluginOptions = {}
4949
) => {
50-
// Copy linked files to the public directory and modify the AST to point to new location of the files.
51-
const visitor = (link) => {
52-
if (
53-
isAbsoluteURL(link.url) ||
54-
isIgnore(
55-
link.url,
56-
checkIgnoreFileExtensions(pluginOptions.ignoreFileExtensions)
57-
)
58-
) {
59-
return
60-
}
6150

62-
const linkPath = Path.join(getNode(markdownNode.parent).dir, link.url)
51+
// Copy a file, then return its new link URL
52+
const copyFile = (relativePath) => {
53+
const linkPath = Path.join(getNode(markdownNode.parent).dir, relativePath)
6354
const linkNode = files.find((file) => {
6455
return file && file.absolutePath ? file.absolutePath === linkPath : false
6556
})
6657

6758
if (!(linkNode && linkNode.absolutePath)) {
68-
return
59+
return relativePath
6960
}
7061

7162
const newPath = Path.join(
@@ -74,18 +65,45 @@ module.exports = (
7465
`${linkNode.relativePath}`
7566
)
7667

77-
link.url = Path.join(linkPrefix || '/', linkNode.relativePath)
78-
if (FsExtra.existsSync(newPath)) {
68+
const newLinkUrl = Path.join(linkPrefix || '/', linkNode.relativePath)
69+
if (!FsExtra.existsSync(newPath)) {
70+
FsExtra.copy(linkPath, newPath, (err) => {
71+
if (err) {
72+
console.error(`error copying file`, err)
73+
}
74+
})
75+
}
76+
77+
return newLinkUrl
78+
}
79+
80+
// Copy linked files to the public directory and modify the AST to point to new location of the files.
81+
const visitor = (link) => {
82+
if (
83+
isAbsoluteURL(link.url) ||
84+
isIgnore(
85+
link.url,
86+
checkIgnoreFileExtensions(pluginOptions.ignoreFileExtensions)
87+
)
88+
) {
7989
return
8090
}
8191

82-
FsExtra.copy(linkPath, newPath, (err) => {
83-
if (err) {
84-
console.error(`error copying file`, err)
85-
}
86-
})
92+
link.url = copyFile(link.url)
8793
}
8894

8995
Visit(markdownAST, `image`, (image) => visitor(image))
9096
Visit(markdownAST, `link`, (link) => visitor(link))
97+
98+
// Copy additional files requested by a copyfiles manifest.
99+
const manifestIndex = markdownAST.children.findIndex(
100+
node => node.type === "code" && node.lang === "copyfiles"
101+
);
102+
if (manifestIndex != -1) {
103+
const filesToCopy = markdownAST.children[manifestIndex].value.split('\n').map(s => s.trim());
104+
filesToCopy.forEach(filename => copyFile(filename))
105+
markdownAST.children = [].concat(
106+
markdownAST.children.slice(0, manifestIndex),
107+
markdownAST.children.slice(manifestIndex + 1))
108+
}
91109
}

src/index.test.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,57 @@ describe('gatsby-remark-copy-relative-linked-files', () => {
240240
expect(FsExtra.copy).not.toHaveBeenCalled()
241241
})
242242
})
243+
244+
describe('manifest', () => {
245+
test('One file', async () => {
246+
const path = 'sample.thumb.jpg'
247+
const markdownAST = remark.parse(`\`\`\`copyfiles\n${path}\n\`\`\``)
248+
249+
await Plugin({
250+
files: getFiles(path),
251+
markdownAST,
252+
markdownNode,
253+
getNode
254+
})
255+
256+
expect(FsExtra.copy).toHaveBeenCalled()
257+
expect(markdownAST.children.findIndex(
258+
node => node.type === "code" && node.lang === "copyfiles"
259+
)).toEqual(-1);
260+
})
261+
262+
test('Two files', async () => {
263+
const path1 = 'report.css'
264+
const path2 = 'diagram.png'
265+
const markdownAST = remark.parse(`\`\`\`copyfiles\n${path1}\n${path2}\n\`\`\``)
266+
267+
await Plugin({
268+
files: getFiles(path1).concat(getFiles(path2)),
269+
markdownAST,
270+
markdownNode,
271+
getNode
272+
})
273+
274+
expect(FsExtra.copy).toHaveBeenNthCalledWith(1, path1, expect.anything(), expect.anything())
275+
expect(FsExtra.copy).toHaveBeenNthCalledWith(2, path2, expect.anything(), expect.anything())
276+
})
277+
278+
test('Tolerates whitespace', async () => {
279+
const path1 = 'report.css'
280+
const path2 = 'diagram.png'
281+
const markdownAST = remark.parse(`\`\`\`copyfiles\n ${path1}\n${path2} \n\`\`\``)
282+
283+
await Plugin({
284+
files: getFiles(path1).concat(getFiles(path2)),
285+
markdownAST,
286+
markdownNode,
287+
getNode
288+
})
289+
290+
expect(FsExtra.copy).toHaveBeenNthCalledWith(1, path1, expect.anything(), expect.anything())
291+
expect(FsExtra.copy).toHaveBeenNthCalledWith(2, path2, expect.anything(), expect.anything())
292+
})
293+
})
243294
})
244295

245296
// For private API

0 commit comments

Comments
 (0)