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

[gatsby-remark-copy-linked-files] Fix bad copy location with route dir and improve tests #2875

Merged
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
48 changes: 41 additions & 7 deletions packages/gatsby-remark-copy-linked-files/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ jest.mock(`fs-extra`, () => {
return {
existsSync: () => false,
copy: jest.fn(),
ensureDir: jest.fn(),
}
})
const Remark = require(`remark`)
Expand All @@ -16,6 +17,8 @@ const remark = new Remark().data(`settings`, {
pedantic: true,
})

const imageURL = markdownAST => markdownAST.children[0].children[0].url

describe(`gatsby-remark-copy-linked-files`, () => {
afterEach(() => {
fsExtra.copy.mockReset()
Expand Down Expand Up @@ -129,32 +132,63 @@ describe(`gatsby-remark-copy-linked-files`, () => {

describe(`options.destinationDir`, () => {
const imagePath = `images/sample-image.gif`
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)

it(`throws an error if the destination directory is not within 'public'`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const invalidDestinationDir = `../destination`
expect.assertions(1)
expect.assertions(2)
return plugin(
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
{
destinationDir: invalidDestinationDir,
}
).catch(e => {
expect(e).toEqual(expect.stringContaining(invalidDestinationDir))
expect(fsExtra.copy).not.toHaveBeenCalled()
})
})

it(`doesn't throw an error if the destination directory is within 'public'`, async () => {
it(`copies file to destinationDir when supplied`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const validDestinationDir = `path/to/dir`
expect.assertions(1)
return plugin(
const expectedNewPath = path.posix.join(
process.cwd(),
`public`,
validDestinationDir,
`/undefined-undefined.gif`
)
expect.assertions(3)
await plugin(
{ files: getFiles(imagePath), markdownAST, markdownNode, getNode },
{
destinationDir: validDestinationDir,
}
).then(v => {
const newPath = v[0]
expect(newPath).toContain(validDestinationDir)
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(
`/path/to/dir/undefined-undefined.gif`
)
})
})

it(`copies file to root dir when not supplied'`, async () => {
const markdownAST = remark.parse(`![some absolute image](${imagePath})`)
const expectedNewPath = path.posix.join(
process.cwd(),
`public`,
`/undefined-undefined.gif`
)
expect.assertions(3)
await plugin({
files: getFiles(imagePath),
markdownAST,
markdownNode,
getNode,
}).then(v => {
expect(v).toBeDefined()
expect(fsExtra.copy).toHaveBeenCalledWith(imagePath, expectedNewPath)
expect(imageURL(markdownAST)).toEqual(`/undefined-undefined.gif`)
})
})
})
Expand Down
77 changes: 44 additions & 33 deletions packages/gatsby-remark-copy-linked-files/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,52 @@ const path = require(`path`)
const _ = require(`lodash`)
const cheerio = require(`cheerio`)
const sizeOf = require(`image-size`)
const pathIsInside = require(`path-is-inside`)

const DEPLOY_DIR = `public`

const invalidDestinationDirMessage = destinationDir =>
`[gatsby-remark-copy-linked-files] You have supplied an invalid destination directory. The destination directory must be within the '${
DEPLOY_DIR
}' directory, but was: '${destinationDir}'`
const invalidDestinationDirMessage = dir =>
`[gatsby-remark-copy-linked-files You have supplied an invalid destination directory. The destination directory must be a child but was: ${
dir
}`

const destinationDirIsValid = destinationDir =>
pathIsInside(path.join(DEPLOY_DIR, destinationDir), DEPLOY_DIR)
// dir must be a child
const destinationDirIsValid = dir => !path.relative(`./`, dir).startsWith(`..`)

const validateDestinationDir = dir =>
!dir || (dir && destinationDirIsValid(dir))

const newFileName = linkNode =>
`${linkNode.name}-${linkNode.internal.contentDigest}.${linkNode.extension}`

const newPath = (linkNode, destinationDir) => {
if (destinationDir) {
return path.posix.join(
process.cwd(),
DEPLOY_DIR,
destinationDir,
newFileName(linkNode)
)
}
return path.posix.join(process.cwd(), DEPLOY_DIR, newFileName(linkNode))
}

const newLinkURL = (linkNode, destinationDir) => {
if (destinationDir) {
return path.posix.join(`/`, destinationDir, newFileName(linkNode))
}
return path.posix.join(`/`, newFileName(linkNode))
}

module.exports = (
{ files, markdownNode, markdownAST, getNode },
pluginOptions = {}
) => {
const defaults = {
ignoreFileExtensions: [`png`, `jpg`, `jpeg`, `bmp`, `tiff`],
destinationDir: `/`,
}

// Validate supplied destination directory
const { destinationDir } = pluginOptions
if (destinationDir && !destinationDirIsValid(destinationDir)) {
if (!validateDestinationDir(destinationDir))
return Promise.reject(invalidDestinationDirMessage(destinationDir))
}

const options = _.defaults(pluginOptions, defaults)

Expand All @@ -53,24 +73,14 @@ module.exports = (
return null
})
if (linkNode && linkNode.absolutePath) {
const newPath = path.posix.join(
process.cwd(),
DEPLOY_DIR,
options.destinationDir,
`${linkNode.internal.contentDigest}.${linkNode.extension}`
)
// Prevent uneeded copying
if (linkPath === newPath) {
return
}
const newFilePath = newPath(linkNode, options.destinationDir)

const relativePath = path.posix.join(
options.destinationDir,
`${linkNode.internal.contentDigest}.${linkNode.extension}`
)
link.url = `${relativePath}`
// Prevent uneeded copying
if (linkPath === newFilePath) return

filesToCopy.set(linkPath, newPath)
const linkURL = newLinkURL(linkNode, options.destinationDir)
link.url = linkURL
filesToCopy.set(linkPath, newFilePath)
}
}
}
Expand Down Expand Up @@ -256,15 +266,16 @@ module.exports = (
})

return Promise.all(
Array.from(filesToCopy, async ([linkPath, newPath]) => {
if (!fsExtra.existsSync(newPath)) {
Array.from(filesToCopy, async ([linkPath, newFilePath]) => {
// Don't copy anything is the file already exists at the location.
if (!fsExtra.existsSync(newFilePath)) {
try {
await fsExtra.copy(linkPath, newPath)
await fsExtra.ensureDir(path.dirname(newFilePath))
await fsExtra.copy(linkPath, newFilePath)
} catch (err) {
console.error(`error copying file`, err)
console.error(`error copy ing file`, err)
}
}
return newPath
})
)
}