chushihua
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const postcss = require('postcss');
|
||||
|
||||
const declProcessor = require('./lib/decl-processor').declProcessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @type {Plugin}
|
||||
*/
|
||||
module.exports = postcss.plugin('postcss-url', (options) => {
|
||||
options = options || {};
|
||||
|
||||
return function(styles, result) {
|
||||
const opts = result.opts;
|
||||
const from = opts.from ? path.dirname(opts.from) : '.';
|
||||
const to = opts.to ? path.dirname(opts.to) : from;
|
||||
|
||||
styles.walkDecls((decl) =>
|
||||
declProcessor(from, to, options, result, decl)
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* @callback PostcssUrl~UrlProcessor
|
||||
* @param {String} from from
|
||||
* @param {String} dirname to dirname
|
||||
* @param {String} oldUrl url
|
||||
* @param {String} to destination
|
||||
* @param {Object} options plugin options
|
||||
* @param {Object} decl postcss declaration
|
||||
* @return {String|undefined} new url or undefined if url is old
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostcssUrl~HashOptions
|
||||
* @property {Function|String} [method=^xxhash32|xxhash64] - hash name or custom function, accepting file content
|
||||
* @see https://github.com/pierrec/js-xxhash
|
||||
* @property {Number} [shrink=8] - shrink hash string
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Decl - postcss decl
|
||||
* @see http://api.postcss.org/Declaration.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} Result - postcss result
|
||||
* @see http://api.postcss.org/Result.html
|
||||
*/
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
'use strict';
|
||||
|
||||
const matchOptions = require('./match-options');
|
||||
const paths = require('./paths');
|
||||
|
||||
const getPathDeclFile = paths.getPathDeclFile;
|
||||
const getDirDeclFile = paths.getDirDeclFile;
|
||||
const prepareAsset = paths.prepareAsset;
|
||||
|
||||
/**
|
||||
* @typedef UrlRegExp
|
||||
* @name UrlRegExp
|
||||
* @desc A regex for match url with parentheses:
|
||||
* (before url)(the url)(after url).
|
||||
* (the url) will be replace with new url, and before and after will remain
|
||||
* @type RegExp
|
||||
*/
|
||||
/**
|
||||
* @type {UrlRegExp[]}
|
||||
*/
|
||||
const URL_PATTERNS = [
|
||||
/(url\(\s*['"]?)([^"')]+)(["']?\s*\))/g,
|
||||
/(AlphaImageLoader\(\s*src=['"]?)([^"')]+)(["'])/g
|
||||
];
|
||||
|
||||
const WITH_QUOTES = /^['"]/;
|
||||
|
||||
/**
|
||||
* Restricted modes
|
||||
*
|
||||
* @type {String[]}
|
||||
*/
|
||||
const PROCESS_TYPES = ['rebase', 'inline', 'copy', 'custom'];
|
||||
|
||||
const getUrlProcessorType = (optionUrl) =>
|
||||
typeof optionUrl === 'function' ? 'custom' : (optionUrl || 'rebase');
|
||||
|
||||
/**
|
||||
* @param {String} optionUrl
|
||||
* @returns {PostcssUrl~UrlProcessor}
|
||||
*/
|
||||
function getUrlProcessor(optionUrl) {
|
||||
const mode = getUrlProcessorType(optionUrl);
|
||||
|
||||
if (PROCESS_TYPES.indexOf(mode) === -1) {
|
||||
throw new Error(`Unknown mode for postcss-url: ${mode}`);
|
||||
}
|
||||
|
||||
return require(`../type/${mode}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PostcssUrl~UrlProcessor} urlProcessor
|
||||
* @param {Result} result
|
||||
* @param {Decl} decl
|
||||
* @returns {Function}
|
||||
*/
|
||||
const wrapUrlProcessor = (urlProcessor, result, decl) => {
|
||||
const warn = (message) => decl.warn(result, message);
|
||||
const addDependency = (file) => result.messages.push({
|
||||
type: 'dependency',
|
||||
file,
|
||||
parent: getPathDeclFile(decl)
|
||||
});
|
||||
|
||||
return (asset, dir, option) =>
|
||||
urlProcessor(asset, dir, option, decl, warn, result, addDependency);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Decl} decl
|
||||
* @returns {RegExp}
|
||||
*/
|
||||
const getPattern = (decl) =>
|
||||
URL_PATTERNS.find((pattern) => pattern.test(decl.value));
|
||||
|
||||
/**
|
||||
* @param {String} url
|
||||
* @param {Dir} dir
|
||||
* @param {Options} options
|
||||
* @param {Result} result
|
||||
* @param {Decl} decl
|
||||
* @returns {String|undefined}
|
||||
*/
|
||||
const replaceUrl = (url, dir, options, result, decl) => {
|
||||
const asset = prepareAsset(url, dir, decl);
|
||||
|
||||
const matchedOptions = matchOptions(asset, options);
|
||||
|
||||
if (!matchedOptions) return;
|
||||
|
||||
const process = (option) => {
|
||||
const wrappedUrlProcessor = wrapUrlProcessor(getUrlProcessor(option.url), result, decl);
|
||||
|
||||
return wrappedUrlProcessor(asset, dir, option);
|
||||
};
|
||||
|
||||
if (Array.isArray(matchedOptions)) {
|
||||
matchedOptions.forEach((option) => asset.url = process(option));
|
||||
} else {
|
||||
asset.url = process(matchedOptions);
|
||||
}
|
||||
|
||||
return asset.url;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {String} from
|
||||
* @param {String} to
|
||||
* @param {PostcssUrl~Options} options
|
||||
* @param {Result} result
|
||||
* @param {Decl} decl
|
||||
* @returns {PostcssUrl~DeclProcessor}
|
||||
*/
|
||||
const declProcessor = (from, to, options, result, decl) => {
|
||||
const dir = { from, to, file: getDirDeclFile(decl) };
|
||||
const pattern = getPattern(decl);
|
||||
|
||||
if (!pattern) return;
|
||||
|
||||
decl.value = decl.value
|
||||
.replace(pattern, (matched, before, url, after) => {
|
||||
const newUrl = replaceUrl(url, dir, options, result, decl);
|
||||
|
||||
if (!newUrl) return matched;
|
||||
|
||||
if (WITH_QUOTES.test(newUrl) && WITH_QUOTES.test(after)) {
|
||||
before = before.slice(0, -1);
|
||||
after = after.slice(1);
|
||||
}
|
||||
|
||||
return `${before}${newUrl}${after}`;
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
replaceUrl,
|
||||
declProcessor
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostcssUrl~Options - postcss-url Options
|
||||
* @property {String} [url=^rebase|inline|copy|custom] - processing mode
|
||||
* @property {Minimatch|RegExp|Function} [filter] - filter assets by relative pathname
|
||||
* @property {String} [assetsPath] - absolute or relative path to copy assets
|
||||
* @property {String|String[]} [basePath] - absolute or relative paths to search, when copy or inline
|
||||
* @property {Number} [maxSize] - max file size in kbytes for inline mode
|
||||
* @property {String} [fallback] - fallback mode if file exceeds maxSize
|
||||
* @property {Boolean} [useHash] - use file hash instead filename
|
||||
* @property {HashOptions} [hashOptions] - params for generating hash name
|
||||
*/
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Optimize encoding SVG files (IE9+, Android 3+)
|
||||
* @see https://codepen.io/tigt/post/optimizing-svgs-in-data-uris
|
||||
*
|
||||
* @param {String} svgContent
|
||||
* @returns {String}
|
||||
*/
|
||||
const optimizedSvgEncode = (svgContent) => {
|
||||
const result = encodeURIComponent(svgContent)
|
||||
.replace(/%3D/g, '=')
|
||||
.replace(/%3A/g, ':')
|
||||
.replace(/%2F/g, '/')
|
||||
.replace(/%22/g, "'")
|
||||
.replace(/%2C/g, ',')
|
||||
.replace(/%3B/g, ';');
|
||||
|
||||
// Lowercase the hex-escapes for better gzipping
|
||||
return result.replace(/(%[0-9A-Z]{2})/g, (matched, AZ) => {
|
||||
return AZ.toLowerCase();
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Encoding file contents to string
|
||||
*
|
||||
* @param {PostcssUrl~File} file
|
||||
* @param {String} [encodeType=base64|encodeURI|encodeURIComponent]
|
||||
* @param {Boolean} [shouldOptimizeURIEncode]
|
||||
* @returns {string}
|
||||
*/
|
||||
|
||||
module.exports = (file, encodeType, shouldOptimizeSvgEncode) => {
|
||||
const dataMime = `data:${file.mimeType}`;
|
||||
|
||||
if (encodeType === 'base64') {
|
||||
return `${dataMime};base64,${file.contents.toString('base64')}`;
|
||||
}
|
||||
|
||||
const encodeFunc = encodeType === 'encodeURI' ? encodeURI : encodeURIComponent;
|
||||
|
||||
const content = file.contents.toString('utf8')
|
||||
// removing new lines
|
||||
.replace(/\n+/g, '');
|
||||
|
||||
let encodedStr = (shouldOptimizeSvgEncode && encodeType === 'encodeURIComponent')
|
||||
? optimizedSvgEncode(content)
|
||||
: encodeFunc(content);
|
||||
|
||||
encodedStr = encodedStr
|
||||
.replace(/%20/g, ' ')
|
||||
.replace(/#/g, '%23');
|
||||
|
||||
return `${dataMime},${encodedStr}`;
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const mime = require('mime');
|
||||
|
||||
const getPathByBasePath = require('./paths').getPathByBasePath;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Options} options
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {Function} warn
|
||||
* @returns {PostcssUrl~File}
|
||||
*/
|
||||
const getFile = (asset, options, dir, warn) => {
|
||||
const paths = options.basePath
|
||||
? getPathByBasePath(options.basePath, dir.from, asset.pathname)
|
||||
: [asset.absolutePath];
|
||||
const filePath = paths.find(fs.existsSync);
|
||||
|
||||
if (!filePath) {
|
||||
warn(`Can't read file '${paths.join()}', ignoring`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
path: filePath,
|
||||
contents: fs.readFileSync(filePath),
|
||||
mimeType: mime.lookup(filePath)
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = getFile;
|
||||
|
||||
/**
|
||||
* @typedef {Object} PostcssUrl~File
|
||||
* @property {String} path
|
||||
* @property {Buffer} contents
|
||||
* @property {String} mimeType
|
||||
*/
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const xxh = require('xxhashjs');
|
||||
const HEXBASE = 16;
|
||||
|
||||
const defaultHashOptions = {
|
||||
method: 'xxhash32',
|
||||
shrink: 8,
|
||||
append: false
|
||||
};
|
||||
|
||||
const getxxhash = (content, options) => {
|
||||
const hashFunc = options.method === 'xxhash32' ? xxh.h32 : xxh.h64;
|
||||
const seed = 0;
|
||||
|
||||
return hashFunc(seed)
|
||||
.update(content)
|
||||
.digest()
|
||||
.toString(HEXBASE);
|
||||
};
|
||||
|
||||
const getHash = (content, options) => {
|
||||
if (options.method && typeof options.method === 'function') {
|
||||
return options.method(content);
|
||||
}
|
||||
|
||||
if (options.method && options.method.indexOf('xxhash') === 0) {
|
||||
return getxxhash(content, options);
|
||||
}
|
||||
|
||||
try {
|
||||
const hashFunc = crypto.createHash(options.method);
|
||||
|
||||
return hashFunc.update(content)
|
||||
.digest('hex');
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function(content, options) {
|
||||
options = options || defaultHashOptions;
|
||||
|
||||
let hash = getHash(content, options);
|
||||
|
||||
if (hash == null) {
|
||||
// bad hash method; fallback to defaults
|
||||
// TODO: warning/error reporting?
|
||||
hash = getHash(content, defaultHashOptions);
|
||||
}
|
||||
|
||||
return options.shrink ? hash.substr(0, options.shrink) : hash;
|
||||
};
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
'use strict';
|
||||
|
||||
const minimatch = require('minimatch');
|
||||
const path = require('path');
|
||||
|
||||
const paths = require('./paths');
|
||||
|
||||
const isUrlShouldBeIgnored = paths.isUrlShouldBeIgnored;
|
||||
|
||||
/**
|
||||
* Returns whether the given asset matches the given pattern
|
||||
* Allways returns true if the given pattern is empty
|
||||
*
|
||||
* @param {PostcssUrl~Asset} asset the processed asset
|
||||
* @param {String|RegExp|Function} pattern A minimatch string,
|
||||
* regular expression or function to test the asset
|
||||
*
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
const matchesFilter = (asset, pattern) => {
|
||||
const relativeToRoot = path.relative(process.cwd(), asset.absolutePath);
|
||||
|
||||
if (typeof pattern === 'string') {
|
||||
pattern = minimatch.filter(pattern);
|
||||
|
||||
return pattern(relativeToRoot);
|
||||
}
|
||||
|
||||
if (pattern instanceof RegExp) {
|
||||
return pattern.test(relativeToRoot);
|
||||
}
|
||||
|
||||
if (pattern instanceof Function) {
|
||||
return pattern(asset);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Matching single option
|
||||
*
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Options} option
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
const matchOption = (asset, option) => {
|
||||
const matched = matchesFilter(asset, option.filter);
|
||||
|
||||
if (!matched) return false;
|
||||
|
||||
return typeof option.url === 'function' || !isUrlShouldBeIgnored(asset.url, option);
|
||||
};
|
||||
|
||||
const isMultiOption = (option) =>
|
||||
option.multi && typeof option.url === 'function';
|
||||
|
||||
/**
|
||||
* Matching options by asset
|
||||
*
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Options|PostcssUrl~Options[]} options
|
||||
* @returns {PostcssUrl~Options|undefined}
|
||||
*/
|
||||
const matchOptions = (asset, options) => {
|
||||
if (!options) return;
|
||||
|
||||
if (Array.isArray(options)) {
|
||||
const optionIndex = options.findIndex((option) => matchOption(asset, option));
|
||||
|
||||
if (optionIndex < 0) return;
|
||||
|
||||
const matchedOption = options[optionIndex];
|
||||
|
||||
// if founded option is last
|
||||
if (optionIndex === options.length - 1) return matchedOption;
|
||||
|
||||
const extendOptions = options
|
||||
.slice(optionIndex + 1)
|
||||
.filter((option) =>
|
||||
(isMultiOption(matchedOption) || isMultiOption(option)) && matchOption(asset, option)
|
||||
);
|
||||
|
||||
return extendOptions.length
|
||||
? [matchedOption].concat(extendOptions)
|
||||
: matchedOption;
|
||||
}
|
||||
|
||||
if (matchOption(asset, options)) return options;
|
||||
};
|
||||
|
||||
module.exports = matchOptions;
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
'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)
|
||||
*/
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const mkdirp = require('mkdirp');
|
||||
|
||||
const calcHash = require('../lib/hash');
|
||||
const paths = require('../lib/paths');
|
||||
const getFile = require('../lib/get-file');
|
||||
|
||||
const getTargetDir = paths.getTargetDir;
|
||||
const getAssetsPath = paths.getAssetsPath;
|
||||
const normalize = paths.normalize;
|
||||
|
||||
const getHashName = (file, options) =>
|
||||
(options && options.append ? (`${path.basename(file.path, path.extname(file.path))}_`) : '')
|
||||
+ calcHash(file.contents, options)
|
||||
+ path.extname(file.path);
|
||||
|
||||
/**
|
||||
* Copy images from readed from url() to an specific assets destination
|
||||
* (`assetsPath`) and fix url() according to that path.
|
||||
* You can rename the assets by a hash or keep the real filename.
|
||||
*
|
||||
* Option assetsPath is require and is relative to the css destination (`to`)
|
||||
*
|
||||
* @type {PostcssUrl~UrlProcessor}
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {PostcssUrl~Option} options
|
||||
* @param {PostcssUrl~Decl} decl
|
||||
* @param {Function} warn
|
||||
* @param {Result} result
|
||||
* @param {Function} addDependency
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
module.exports = function processCopy(asset, dir, options, decl, warn, result, addDependency) {
|
||||
if (!options.assetsPath && dir.from === dir.to) {
|
||||
warn('Option `to` of postcss is required, ignoring');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const file = getFile(asset, options, dir, warn);
|
||||
|
||||
if (!file) return;
|
||||
|
||||
const assetRelativePath = options.useHash
|
||||
? getHashName(file, options.hashOptions)
|
||||
: asset.relativePath;
|
||||
|
||||
const targetDir = getTargetDir(dir);
|
||||
const newAssetBaseDir = getAssetsPath(targetDir, options.assetsPath);
|
||||
const newAssetPath = path.join(newAssetBaseDir, assetRelativePath);
|
||||
const newRelativeAssetPath = normalize(
|
||||
path.relative(targetDir, newAssetPath)
|
||||
);
|
||||
|
||||
mkdirp.sync(path.dirname(newAssetPath));
|
||||
|
||||
if (!fs.existsSync(newAssetPath)) {
|
||||
fs.writeFileSync(newAssetPath, file.contents);
|
||||
}
|
||||
|
||||
addDependency(file.path);
|
||||
|
||||
return `${newRelativeAssetPath}${asset.search}${asset.hash}`;
|
||||
};
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Transform url() based on a custom callback
|
||||
*
|
||||
* @type {PostcssUrl~UrlProcessor}
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {PostcssUrl~Option} options
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
module.exports = function getCustomProcessor(asset, dir, options) {
|
||||
return options.url.apply(null, arguments);
|
||||
};
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
const processCopy = require('./copy');
|
||||
const processRebase = require('./rebase');
|
||||
|
||||
const encodeFile = require('../lib/encode');
|
||||
const getFile = require('../lib/get-file');
|
||||
|
||||
/**
|
||||
* @param {String} originUrl
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {PostcssUrl~Option} options
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
function processFallback(originUrl, dir, options) {
|
||||
if (typeof options.fallback === 'function') {
|
||||
return options.fallback.apply(null, arguments);
|
||||
}
|
||||
switch (options.fallback) {
|
||||
case 'copy':
|
||||
return processCopy.apply(null, arguments);
|
||||
case 'rebase':
|
||||
return processRebase.apply(null, arguments);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inline image in url()
|
||||
*
|
||||
* @type {PostcssUrl~UrlProcessor}
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {PostcssUrl~Options} options
|
||||
* @param {PostcssUrl~Decl} decl
|
||||
* @param {Function} warn
|
||||
* @param {Result} result
|
||||
* @param {Function} addDependency
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
module.exports = function(asset, dir, options, decl, warn, result, addDependency) {
|
||||
const file = getFile(asset, options, dir, warn);
|
||||
|
||||
if (!file) return;
|
||||
|
||||
if (!file.mimeType) {
|
||||
warn(`Unable to find asset mime-type for ${file.path}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const maxSize = (options.maxSize || 0) * 1024;
|
||||
|
||||
if (maxSize) {
|
||||
const stats = fs.statSync(file.path);
|
||||
|
||||
if (stats.size >= maxSize) {
|
||||
return processFallback.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
const isSvg = file.mimeType === 'image/svg+xml';
|
||||
const defaultEncodeType = isSvg ? 'encodeURIComponent' : 'base64';
|
||||
const encodeType = options.encodeType || defaultEncodeType;
|
||||
|
||||
// Warn for svg with hashes/fragments
|
||||
if (isSvg && asset.hash && !options.ignoreFragmentWarning) {
|
||||
// eslint-disable-next-line max-len
|
||||
warn(`Image type is svg and link contains #. Postcss-url cant handle svg fragments. SVG file fully inlined. ${file.path}`);
|
||||
}
|
||||
|
||||
addDependency(file.path);
|
||||
|
||||
const optimizeSvgEncode = isSvg && options.optimizeSvgEncode;
|
||||
const encodedStr = encodeFile(file, encodeType, optimizeSvgEncode);
|
||||
const resultValue = options.includeUriFragment && asset.hash
|
||||
? encodedStr + asset.hash
|
||||
: encodedStr;
|
||||
|
||||
// wrap url by quotes if percent-encoded svg
|
||||
return isSvg && encodeType !== 'base64' ? `"${resultValue}"` : resultValue;
|
||||
};
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const paths = require('../lib/paths');
|
||||
const normalize = paths.normalize;
|
||||
const getAssetsPath = paths.getAssetsPath;
|
||||
|
||||
/**
|
||||
* Fix url() according to source (`from`) or destination (`to`)
|
||||
*
|
||||
* @type {PostcssUrl~UrlProcessor}
|
||||
* @param {PostcssUrl~Asset} asset
|
||||
* @param {PostcssUrl~Dir} dir
|
||||
* @param {PostcssUrl~Option} options
|
||||
*
|
||||
* @returns {String|Undefined}
|
||||
*/
|
||||
module.exports = function(asset, dir, options) {
|
||||
const dest = getAssetsPath(dir.to, options && options.assetsPath || '');
|
||||
const rebasedUrl = normalize(
|
||||
path.relative(dest, asset.absolutePath)
|
||||
);
|
||||
|
||||
return `${rebasedUrl}${asset.search}${asset.hash}`;
|
||||
};
|
||||
Reference in New Issue
Block a user