lib/url.js
- import moment from 'moment';
- import slug from 'slug';
- import path from 'path';
-
- /**
- * Are we running within a dist build (i.e. pre-compiled)?
- * @type {boolean} True if we're running in the dist folder.
- */
- const isDistBuild = __dirname.indexOf('dist') > -1;
-
- // Cached slug options.
- let slugOptions;
-
- /**
- * @param {string} filePath A file path.
- * @param {Array.<string>} markdownExtensions Array of known markdown file
- * extensions.
- * @return {number} Index of the filepath extension.
- */
- function getMarkdownExtensionIndexForFilePath(filePath, markdownExtensions) {
- // Get file extension of file. i.e. 'post.md' would give 'md'.
- const fileExtension = path.extname(filePath).replace(/^\./, '');
- return markdownExtensions.indexOf(fileExtension);
- }
-
- const Url = {
- /**
- * Interpolates variables into a permalink structure.
- * @example
- * // returns '/hello-world/'
- * interpolatePermalink('/:title/', {
- * title: 'hello-world'
- * });
- * @param {string} permalink A permalink template.
- * @param {Object} context An object with keys that if matched to the
- * permalink will have the value interpolated to the string.
- * @return {string} Actual permalink value.
- */
- interpolatePermalink(permalink, context) {
- // eslint-disable-next-line no-useless-escape
- const PERMALINK_REGEX = /:(\w+[\|A-Z]*)/g;
-
- const params = permalink.match(PERMALINK_REGEX);
-
- // If we found no tags in the permalink then just return the given string.
- if (!params) {
- return permalink;
- }
-
- let result = permalink;
-
- params.forEach(param => {
- // Replace ':title' -> 'title'.
- let paramKey = param.substr(1);
-
- let paramPipe;
- if (paramKey.includes('|')) {
- [paramKey, paramPipe] = paramKey.split('|');
- }
-
- let paramValue = context[paramKey];
-
- if (paramValue) {
- if (paramPipe) {
- paramValue = moment.utc(paramValue).format(paramPipe);
- }
- const sanitized = Url.slug(paramValue);
- result = result.replace(param, sanitized);
- } else {
- throw new Error(
- `${'interpolatePermalink: could not find param value ' +
- 'for key: '}${paramKey}`
- );
- }
- });
-
- return result;
- },
-
- /**
- * Wrapper around the slug module. Handles taking a string and making it
- * into a slug, a URL safe string.
- * @param {string} str String to slugify.
- * @param {Object} options Slug options.
- * @return {string} Slugified string.
- */
- slug(str, options) {
- return slug(str, {
- ...slugOptions,
- options,
- });
- },
-
- /**
- * Set slug options to be used by Url.slug.
- * @param {Object} options Options
- */
- setSlugOptions(options) {
- slugOptions = options;
- },
-
- /**
- * Check if given filePath matches a markdown extension.
- * @param {string} filePath A file path.
- * @param {Array.<string>} markdownExtensions Array of known markdown file
- * extensions.
- * @return {boolean} Whether this filePath matches any of our known markdown
- * extensions.
- */
- pathHasMarkdownExtension(filePath, markdownExtensions) {
- return (
- getMarkdownExtensionIndexForFilePath(filePath, markdownExtensions) > -1
- );
- },
-
- /**
- * Replaces a markdown file path with `.html` if it's a known markdown file.
- * @param {string} filePath A file path.
- * @param {Array.<string>} markdownExtensions Array of known markdown file
- * extensions.
- * @return {string} Modified file path.
- */
- replaceMarkdownExtension(filePath, markdownExtensions) {
- const index = getMarkdownExtensionIndexForFilePath(
- filePath,
- markdownExtensions
- );
-
- let result = filePath;
-
- // Is this file's extension one of our known markdown extensions?
- if (index > -1) {
- const foundExtension = markdownExtensions[index];
- result = filePath.replace(new RegExp(`.${foundExtension}$`), '.html');
- }
-
- return result;
- },
-
- /**
- * When writing to the file system update the permalink so that it renders
- * correctly.
- * @example
- * // returns '/hello-world/index.html'
- * Url.makeUrlFileSystemSafe('/hello-world');
- * @param {string} url Url to make file system safe.
- * @return {string} Safe url.
- */
- makeUrlFileSystemSafe(url) {
- let result = url;
-
- // If the url does not end with an extension then we need to modify the URL.
- if (!path.extname(url)) {
- // If we don't have a leading / then add it.
- if (!url.startsWith('/')) {
- result = `/${url}`;
- }
-
- // If we don't have a trailing / then add it.
- if (!url.endsWith('/')) {
- result += '/';
- }
-
- // Append the default file name.
- result += 'index.html';
- }
-
- return result;
- },
-
- /**
- * Given a URL that ends with 'index.html' it'll strip it off and return the
- * resulting value. Useful when creating URLs in a template.
- * @param {string} url Url to augment.
- * @return {string} Augmented url.
- */
- makePretty(url) {
- const makePrettyRegEx = /\/index.html$/;
- return url.replace(makePrettyRegEx, '/');
- },
-
- /**
- * Resolve a path from the root of the project, taking into account
- * the relative depth we need to go to get to the root of the project,
- * depending if we're in a pre-compiled build or not.
- * @param {...string} args Splat of strings.
- * @return {string} Full path.
- */
- pathFromRoot(...args) {
- // Push relative distance from root of project.
- args.unshift(isDistBuild ? '../../' : '../');
-
- // Add dirname relative from.
- args.unshift(__dirname);
-
- return path.resolve(...args);
- },
- };
-
- export default Url;