Find files which are linked to from markdown and copy them to the public directory
npx @tessl/cli install tessl/npm-gatsby-remark-copy-linked-files@6.15.0A Gatsby remark plugin that automatically finds files linked to/from markdown and copies them to the public directory with content-based hashing and configurable destination paths.
npm install gatsby-remark-copy-linked-filesconst plugin = require("gatsby-remark-copy-linked-files");Configure the plugin in your gatsby-config.js as part of gatsby-transformer-remark:
module.exports = {
plugins: [
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-copy-linked-files`,
options: {
destinationDir: `downloads`,
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
},
},
],
},
},
],
}Sample markdown file:
---
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)The my-awesome-pdf.pdf file will be copied to the public folder and the generated HTML will be updated to point to the new location.
Main plugin function that processes markdown AST and copies linked files to the public directory.
/**
* Main plugin function for gatsby-remark-copy-linked-files
* @param {Object} context - Gatsby remark context object
* @param {Array} context.files - Array of all file nodes available in Gatsby
* @param {Object} context.markdownNode - The current markdown node being processed
* @param {Object} context.markdownAST - Abstract Syntax Tree of the markdown content
* @param {string} [context.pathPrefix] - Path prefix for the site (optional)
* @param {Function} context.getNode - Function to get Gatsby nodes by ID
* @param {Object} [pluginOptions={}] - Plugin configuration options
* @param {string|Function} [pluginOptions.destinationDir] - Controls where files are copied
* @param {Array<string>} [pluginOptions.ignoreFileExtensions] - File extensions to ignore
* @returns {Promise<void>} Promise that resolves when all files have been copied
*/
module.exports = (
{ files, markdownNode, markdownAST, pathPrefix, getNode },
pluginOptions = {}
) => Promise<void>;Controls the destination directory structure for copied files.
type DestinationDir = string | ((fileData: FileData) => string) | undefined;
interface FileData {
/** File name without extension */
name: string;
/** Content digest hash for uniqueness */
hash: string;
/** Full path to source file */
absolutePath: string;
}String format: Simple directory path that will contain the default content-hash structure.
Function format: Custom function to generate destination paths using file metadata.
Default behavior: If undefined, files are saved as contentHash/fileName.ext in the public root.
Usage Examples:
// Simple directory path
destinationDir: "downloads"
// Result: public/downloads/2a0039f3a61f4510f41678438e4c863a/my-file.pdf
// Custom function using file name only
destinationDir: f => `${f.name}`
// Result: public/my-file.pdf
// Custom function using hash only
destinationDir: f => `${f.hash}`
// Result: public/2a0039f3a61f4510f41678438e4c863a.pdf
// Complex custom path
destinationDir: f => `downloads/${f.name}/${f.hash}`
// Result: public/downloads/my-file/2a0039f3a61f4510f41678438e4c863a.pdfImportant: The destination must not point outside the public folder. Function expressions must include either name or hash properties to prevent filename collisions.
Array of file extensions to ignore during processing.
/**
* File extensions to ignore during processing
* @type {Array<string>}
* @default ['png', 'jpg', 'jpeg', 'bmp', 'tiff']
*/
ignoreFileExtensions: string[]By default, common image formats are ignored as they're typically handled by gatsby-remark-images. Set to an empty array [] to process all file types.
The plugin processes the following markdown and HTML elements:
<!-- Markdown link syntax -->
[Link text](file.pdf)
<!-- Markdown image syntax -->

<!-- Reference-style links -->
[Link text][ref]
[ref]: file.pdf<!-- Image tags -->
<img src="image.png" alt="description" />
<!-- Video tags -->
<video src="video.mp4" controls></video>
<video poster="thumbnail.jpg">
<source src="video.webm" type="video/webm" />
<source src="video.mp4" type="video/mp4" />
</video>
<!-- Audio tags -->
<audio src="audio.mp3" controls></audio>
<audio>
<source src="audio.ogg" type="audio/ogg" />
<source src="audio.mp3" type="audio/mpeg" />
</audio>
<!-- Anchor tags -->
<a href="document.pdf">Download</a>
<!-- Flash embed objects -->
<object>
<param name="movie" value="animation.swf" />
</object>
<object>
<param name="src" value="media.flv" />
</object>The plugin performs the following operations:
internal.contentDigest) to ensure unique paths and prevent collisionsfs-extraprobe-image-size and adds width/height attributes if missingdestinationDir points outside public folder using path-is-inside validationThe plugin relies on the following Node.js packages:
unist-util-visit: AST traversal and manipulationis-relative-url: URL validation and relative path detectionfs-extra: Enhanced file system operations with promisespath: Node.js path manipulation utilitiespath-is-inside: Validation to ensure paths stay within allowed directorieslodash: Utility functions for object manipulationcheerio: Server-side HTML parsing and manipulationprobe-image-size: Image dimension detection without full loading