paths.js 3.96 KB
'use strict';

const path = require('path');
const url = require('url');

/**
 * Normalazing result url, before replace decl value
 *
 * @param {String} assetUrl
 * @returns {String}
 */
const normalize = (assetUrl) => {
    assetUrl = path.normalize(assetUrl);

    return path.sep === '\\' ? assetUrl.replace(/\\/g, '\/') : assetUrl;
};

/**
 * @param {String} assetUrl
 * @returns {Boolean}
 */
const isUrlWithoutPathname = (assetUrl) => {
    return assetUrl[0] === '#'
        || assetUrl.indexOf('%23') === 0
        || assetUrl.indexOf('data:') === 0
        || /^[a-z]+:\/\//.test(assetUrl)
        || /^\/\//.test(assetUrl);
};

/**
 * Check if url is absolute, hash or data-uri
 *
 * @param {String} assetUrl
 * @param {PostcssUrl~Options} options
 * @returns {Boolean}
 */
const isUrlShouldBeIgnored = (assetUrl, options) => {
    const isAbsolutePath = assetUrl[0] === '/';
    const isStartsWithTilde = assetUrl[0] === '~';

    return isUrlWithoutPathname(assetUrl) || ((isAbsolutePath || isStartsWithTilde) && !options.basePath);
};

/**
 * @param {String} baseDir - absolute target path
 * @param {String} assetsPath - extend asset path, can be absolute path
 * @param {String} relative - current relative asset path
 * @returns {String}
 */
const getAssetsPath = (baseDir, assetsPath, relative) =>
    path.resolve(baseDir, assetsPath || '', relative || '');

/**
 * Target path, output base dir
 *
 * @param {Dir} dir
 * @returns {String}
 */
const getTargetDir = (dir) =>
    dir.from !== dir.to ? dir.to : process.cwd();

/**
 * Stylesheet file path from decl
 *
 * @param {Decl} decl
 * @returns {String}
 */
const getPathDeclFile = (decl) =>
    decl.source && decl.source.input && decl.source.input.file;

/**
 * Stylesheet file dir from decl
 *
 * @param {Decl} decl
 * @returns {String}
 */
const getDirDeclFile = (decl) => {
    const filename = getPathDeclFile(decl);

    return filename ? path.dirname(filename) : process.cwd();
};

/**
 * Returns paths list, where we can find assets file
 *
 * @param {String[]|String} basePath - base paths where trying search to assets file
 * @param {Dir} dirFrom
 * @param {String} relPath - relative asset path
 * @returns {String[]}
 */
const getPathByBasePath = (basePath, dirFrom, relPath) => {
    if (relPath[0] === '/') {
        relPath = `.${relPath}`;
    }

    basePath = !Array.isArray(basePath) ? [basePath] : basePath;

    return basePath.map((pathItem) =>
        getAssetsPath(dirFrom, pathItem, relPath)
    );
};

/**
 * Preparing asset paths and data
 *
 * @param {String} assetUrl
 * @param {PostcssUrl~Dir} dir
 * @param {Decl} decl
 * @returns {PostcssUrl~Asset}
 */
const prepareAsset = (assetUrl, dir, decl) => {
    const parsedUrl = url.parse(assetUrl);
    const pathname = !isUrlWithoutPathname(assetUrl) ? parsedUrl.pathname : null;
    const absolutePath = pathname
        ? path.resolve(path.join(dir.file, pathname))
        : getPathDeclFile(decl);

    return {
        url: assetUrl,
        originUrl: assetUrl,
        pathname,
        absolutePath: absolutePath || dir.from,
        relativePath: absolutePath ? path.relative(dir.from, absolutePath) : '.',
        search: (parsedUrl.search || ''),
        hash: (parsedUrl.hash || '')
    };
};

module.exports = {
    normalize,
    prepareAsset,
    getAssetsPath,
    getDirDeclFile,
    getPathDeclFile,
    getTargetDir,
    getPathByBasePath,
    isUrlShouldBeIgnored
};

/**
 * @typedef {Object} PostcssUrl~Asset
 * @property {String} url - origin asset url
 * @property {String} name - parsed asset filename
 * @property {String} absolutePath - absolute asset path
 * @property {String} relativePath - relative asset path (relative to target dir)
 * @property {String} search - search from url, ex. ?query=1
 * @property {String} hash - hash from url
 */

/**
 * @typedef {Object} PostcssUrl~Dir
 * @property {String} from - dirname from postcss option 'from'
 * @property {String} to - dirname from postcss option 'to'
 * @property {String} file - decl file dirname (css file)
 */