* Allow for relative paths of media files fixes #325 * Switch to calculating the relative path based on collection The required relative path is now calculated depending on the location of the collection of the current entry having the media inserted into. And the configuration option has now been changed to a boolean flag. This allows collections to not neccesarily all be in the same location relative to the media folder, and simplifies config. * Clean up code and fix linting * Add unit tests to resolveMediaFilename() * Rework insertMedia action to fetch own config This moves more of the media path resolution logic into the action which makes it easier to unit test * Add unit tests for the mediaLibrary.insertMedia action * yarn run format * add dependabot config (#2580)
129 lines
4.0 KiB
JavaScript
129 lines
4.0 KiB
JavaScript
import getRelativePath from 'get-relative-path';
|
|
|
|
const absolutePath = new RegExp('^(?:[a-z]+:)?//', 'i');
|
|
const normalizePath = path => path.replace(/[\\/]+/g, '/');
|
|
|
|
export function resolvePath(path, basePath) {
|
|
// No path provided, skip
|
|
if (!path) return null;
|
|
|
|
// It's an absolute path.
|
|
if (absolutePath.test(path)) return path;
|
|
|
|
if (path.indexOf('/') === -1) {
|
|
// It's a single file name, no directories. Prepend public folder
|
|
return normalizePath(`/${basePath}/${path}`);
|
|
}
|
|
|
|
// It's a relative path. Prepend a forward slash.
|
|
return normalizePath(`/${path}`);
|
|
}
|
|
|
|
/**
|
|
* Take a media filename and resolve it with respect to a
|
|
* certain collection entry, either as an absolute URL, or
|
|
* a path relative to the collection entry's folder.
|
|
*
|
|
* @param {*} filename the filename of the media item within the media_folder
|
|
* @param {*} options how the filename should be resolved, see examples below:
|
|
*
|
|
* @example Resolving to publicly accessible URL
|
|
* mediaFilenameToUse('image.jpg', {
|
|
* publicFolder: '/static/assets' // set by public_folder
|
|
* }) // -> "/static/assets/image.jpg"
|
|
*
|
|
* @example Resolving URL relatively to a specific collection entry
|
|
* mediaFilenameToUse('image.jpg', {
|
|
* mediaFolder: '/content/media', // set by media_folder
|
|
* collectionFolder: 'content/posts'
|
|
* }) // -> "../media/image.jpg"
|
|
*
|
|
*/
|
|
export function resolveMediaFilename(filename, options) {
|
|
if (options.publicFolder) {
|
|
return resolvePath(filename, options.publicFolder);
|
|
} else if (options.mediaFolder && options.collectionFolder) {
|
|
const media = normalizePath(`/${options.mediaFolder}/${filename}`);
|
|
const collection = normalizePath(`/${options.collectionFolder}/`);
|
|
return getRelativePath(collection, media);
|
|
}
|
|
throw new Error('incorrect usage');
|
|
}
|
|
|
|
/**
|
|
* Return the last portion of a path. Similar to the Unix basename command.
|
|
* @example Usage example
|
|
* path.basename('/foo/bar/baz/asdf/quux.html')
|
|
* // returns
|
|
* 'quux.html'
|
|
*
|
|
* path.basename('/foo/bar/baz/asdf/quux.html', '.html')
|
|
* // returns
|
|
* 'quux'
|
|
*/
|
|
export function basename(p, ext = '') {
|
|
// Special case: Normalize will modify this to '.'
|
|
if (p === '') {
|
|
return p;
|
|
}
|
|
// Normalize the string first to remove any weirdness.
|
|
p = normalizePath(p);
|
|
// Get the last part of the string.
|
|
const sections = p.split('/');
|
|
const lastPart = sections[sections.length - 1];
|
|
// Special case: If it's empty, then we have a string like so: foo/
|
|
// Meaning, 'foo' is guaranteed to be a directory.
|
|
if (lastPart === '' && sections.length > 1) {
|
|
return sections[sections.length - 2];
|
|
}
|
|
// Remove the extension, if need be.
|
|
if (ext.length > 0) {
|
|
const lastPartExt = lastPart.substr(lastPart.length - ext.length);
|
|
if (lastPartExt === ext) {
|
|
return lastPart.substr(0, lastPart.length - ext.length);
|
|
}
|
|
}
|
|
return lastPart;
|
|
}
|
|
|
|
/**
|
|
* Return the extension of the path, from the last '.' to end of string in the
|
|
* last portion of the path. If there is no '.' in the last portion of the path
|
|
* or the first character of it is '.', then it returns an empty string.
|
|
* @example Usage example
|
|
* path.fileExtensionWithSeparator('index.html')
|
|
* // returns
|
|
* '.html'
|
|
*/
|
|
export function fileExtensionWithSeparator(p) {
|
|
p = normalizePath(p);
|
|
const sections = p.split('/');
|
|
p = sections.pop();
|
|
// Special case: foo/file.ext/ should return '.ext'
|
|
if (p === '' && sections.length > 0) {
|
|
p = sections.pop();
|
|
}
|
|
if (p === '..') {
|
|
return '';
|
|
}
|
|
const i = p.lastIndexOf('.');
|
|
if (i === -1 || i === 0) {
|
|
return '';
|
|
}
|
|
return p.substr(i);
|
|
}
|
|
|
|
/**
|
|
* Return the extension of the path, from after the last '.' to end of string in the
|
|
* last portion of the path. If there is no '.' in the last portion of the path
|
|
* or the first character of it is '.', then it returns an empty string.
|
|
* @example Usage example
|
|
* path.fileExtension('index.html')
|
|
* // returns
|
|
* 'html'
|
|
*/
|
|
export function fileExtension(p) {
|
|
const ext = fileExtensionWithSeparator(p);
|
|
return ext === '' ? ext : ext.substr(1);
|
|
}
|