Skip to content

Commit 1c5a0a3

Browse files
committed
Implement a plugin
1 parent 5461807 commit 1c5a0a3

File tree

9 files changed

+499
-1
lines changed

9 files changed

+499
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ typings/
5959

6060
# next.js build output
6161
.next
62+
63+
/*.js
64+
!babel.config.js
65+
package-lock.json

.prettierrc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"arrowParens": "always",
3+
"semi": false,
4+
"singleQuote": true
5+
}

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# ChangeLog
2+
3+
## v1.0.0
4+
5+
- First release

LICENSE

100644100755
File mode changed.

README.md

100644100755
Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,86 @@
1-
# npm-gatsby-remark-copy-relative-linked-files
1+
# gatsby-remark-copy-relative-linked-files
2+
3+
[![Support Node of LTS](https://img.shields.io/badge/node-LTS-brightgreen.svg)](https://nodejs.org/)
4+
[![npm version](https://badge.fury.io/js/gatsby-remark-copy-relative-linked-files.svg)](https://badge.fury.io/js/gatsby-remark-copy-relative-linked-files)
5+
[![Build Status](https://travis-ci.org/akabekobeko/npm-gatsby-remark-copy-relative-linked-files.svg?branch=master)](https://travis-ci.org/akabekobeko/npm-gatsby-remark-copy-relative-linked-files)
6+
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
7+
28
Copies local files relative linked to/from markdown to your `public` folder with preserve directory structure.
9+
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.
11+
12+
## Install
13+
14+
```
15+
$ npm install gatsby-remark-copy-relative-linked-files
16+
```
17+
18+
## How to use
19+
20+
```js
21+
// In your gatsby-config.js
22+
plugins: [
23+
{
24+
resolve: 'gatsby-transformer-remark',
25+
options: {
26+
plugins: ['gatsby-remark-copy-relative-linked-files']
27+
}
28+
}
29+
]
30+
```
31+
32+
Then in your Markdown files, simply reference the files.
33+
34+
### E.g.
35+
36+
```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)
48+
```
49+
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.
51+
52+
The copy target is a relative link. Therefore, links starting with `...://` are excluded. In this example `not-copy.rar` is not copied.
53+
54+
## Options
55+
56+
### ignoreFileExtensions
57+
58+
Specify the file extension to be excluded from copying.
59+
60+
```js
61+
// In your `gatsby-config.js`
62+
plugins: [
63+
{
64+
resolve: 'gatsby-transformer-remark',
65+
options: {
66+
plugins: [
67+
{
68+
resolve: 'gatsby-remark-copy-relative-linked-files',
69+
options: {
70+
// With this specification, `*.pdf` and '*.d.ts' will not be copied.
71+
ignoreFileExtensions: ['.pdf', '.d.ts']
72+
}
73+
}
74+
]
75+
}
76+
}
77+
]
78+
```
79+
80+
# ChangeLog
81+
82+
- [CHANGELOG](CHANGELOG.md)
83+
84+
# License
85+
86+
- [MIT](LICENSE.txt)

babel.config.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = (api) => {
2+
api.cache(true)
3+
return {
4+
presets: [
5+
[
6+
'@babel/preset-env',
7+
{
8+
targets: {
9+
node: '10'
10+
}
11+
}
12+
]
13+
]
14+
}
15+
}

package.json

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
{
2+
"name": "gatsby-remark-copy-relative-linked-files",
3+
"description": "Copies local files relative linked to/from markdown to your public folder with preserve directory structure.",
4+
"version": "1.0.0",
5+
"author": "akabeko (http://akabeko.me/)",
6+
"license": "MIT",
7+
"homepage": "https://github.com/akabekobeko/npm-gatsby-remark-copy-relative-linked-files#readme",
8+
"engines": {
9+
"node": ">= 10"
10+
},
11+
"main": "index.js",
12+
"repository": {
13+
"type": "git",
14+
"url": "git+https://github.com/akabekobeko/npm-gatsby-remark-copy-relative-linked-files.git"
15+
},
16+
"keywords": [
17+
"gatsby",
18+
"gatsby-plugin",
19+
"remark",
20+
"copy"
21+
],
22+
"bugs": {
23+
"url": "https://github.com/akabekobeko/npm-gatsby-remark-copy-relative-linked-files/issues"
24+
},
25+
"jest": {
26+
"verbose": true
27+
},
28+
"scripts": {
29+
"test": "jest",
30+
"build": "babel src --out-dir . --ignore **/*.test.js",
31+
"prepare": "cross-env NODE_ENV=production npm run build",
32+
"watch": "babel -w src --out-dir . --ignore **/*.test.js"
33+
},
34+
"dependencies": {
35+
"fs-extra": "^7.0.1",
36+
"unist-util-visit": "^1.4.0"
37+
},
38+
"devDependencies": {
39+
"@babel/cli": "^7.4.3",
40+
"@babel/core": "^7.4.3",
41+
"@babel/preset-env": "^7.4.3",
42+
"cross-env": "^5.2.0",
43+
"jest": "^24.7.1",
44+
"remark": "^10.0.1",
45+
"rewire": "^4.0.1"
46+
}
47+
}

src/index.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
const Visit = require('unist-util-visit')
2+
const FsExtra = require('fs-extra')
3+
const Path = require('path')
4+
5+
/**
6+
* Checks if the specified URL is an absolute path.
7+
* @param {string} url URL.
8+
* @returns {boolean} `true` if the URL is an absolute path.
9+
* @throws `url` type is not string.
10+
* @see https://stackoverflow.com/questions/10687099/how-to-test-if-a-url-string-is-absolute-or-relative
11+
*/
12+
const isAbsoluteURL = (url) => {
13+
if (typeof url !== 'string') {
14+
throw new Error('`url` type is not string.')
15+
}
16+
17+
return /^(?:[a-z]+:)?\/\//i.test(url)
18+
}
19+
20+
/**
21+
* Check that the file with the specified URL is to be ignored.
22+
* @param {string} url URL.
23+
* @param {string[]} extentions File extensions.
24+
* @returns {boolean} `true` if ignored
25+
*/
26+
const isIgnore = (url, extentions) => {
27+
return Array.isArray(extentions)
28+
? extentions.some((ext) => url.endsWith(ext))
29+
: false
30+
}
31+
32+
module.exports = (
33+
{ files, linkPrefix, markdownNode, markdownAST, getNode },
34+
pluginOptions = {}
35+
) => {
36+
// Copy linked files to the public directory and modify the AST to point to new location of the files.
37+
const visitor = (link) => {
38+
if (
39+
isAbsoluteURL(link.url) ||
40+
isIgnore(link.url, pluginOptions.ignoreFileExtensions)
41+
) {
42+
return
43+
}
44+
45+
const linkPath = Path.join(getNode(markdownNode.parent).dir, link.url)
46+
const linkNode = files.find((file) => {
47+
return file && file.absolutePath ? file.absolutePath === linkPath : false
48+
})
49+
50+
if (!(linkNode && linkNode.absolutePath)) {
51+
return
52+
}
53+
54+
const newPath = Path.join(
55+
process.cwd(),
56+
'public',
57+
`${linkNode.relativePath}`
58+
)
59+
60+
link.url = Path.join(linkPrefix || '/', linkNode.relativePath)
61+
if (FsExtra.existsSync(newPath)) {
62+
return
63+
}
64+
65+
FsExtra.copy(linkPath, newPath, (err) => {
66+
if (err) {
67+
console.error(`error copying file`, err)
68+
}
69+
})
70+
}
71+
72+
Visit(markdownAST, `image`, (image) => visitor(image))
73+
Visit(markdownAST, `link`, (link) => visitor(link))
74+
}

0 commit comments

Comments
 (0)