Skip to content

Commit

Permalink
Merge pull request #134 from MrMeison/migrete-to-async-api
Browse files Browse the repository at this point in the history
Promise for everything
  • Loading branch information
sergcen committed Apr 16, 2019
2 parents 1a7496e + 3f3dddf commit 99dde55
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 195 deletions.
5 changes: 4 additions & 1 deletion src/index.js
Expand Up @@ -13,13 +13,16 @@ module.exports = postcss.plugin('postcss-url', (options) => {
options = options || {};

return function(styles, result) {
const promises = [];
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)
promises.push(declProcessor(from, to, options, result, decl))
);

return Promise.all(promises);
};
});

Expand Down
54 changes: 39 additions & 15 deletions src/lib/decl-processor.js
Expand Up @@ -80,28 +80,42 @@ const getPattern = (decl) =>
* @param {Options} options
* @param {Result} result
* @param {Decl} decl
* @returns {String|undefined}
* @returns {Promise<String|undefined>}
*/
const replaceUrl = (url, dir, options, result, decl) => {
const asset = prepareAsset(url, dir, decl);

const matchedOptions = matchOptions(asset, options);

if (!matchedOptions) return;
if (!matchedOptions) return Promise.resolve();

const process = (option) => {
const wrappedUrlProcessor = wrapUrlProcessor(getUrlProcessor(option.url), result, decl);

return wrappedUrlProcessor(asset, dir, option);
};

let resultPromise = Promise.resolve();

if (Array.isArray(matchedOptions)) {
matchedOptions.forEach((option) => asset.url = process(option));
for (let i = 0; i < matchedOptions.length; i++) {
resultPromise = resultPromise
.then(() => process(matchedOptions[i]))
.then((newUrl) => {
asset.url = newUrl;

return newUrl;
});
}
} else {
asset.url = process(matchedOptions);
resultPromise = process(matchedOptions);
}

return asset.url;
return resultPromise.then((newUrl) => {
asset.url = newUrl;

return newUrl;
});
};

/**
Expand All @@ -110,31 +124,41 @@ const replaceUrl = (url, dir, options, result, decl) => {
* @param {PostcssUrl~Options} options
* @param {Result} result
* @param {Decl} decl
* @returns {PostcssUrl~DeclProcessor}
* @returns {Promise<PostcssUrl~DeclProcessor>}
*/
const declProcessor = (from, to, options, result, decl) => {
const dir = { from, to, file: getDirDeclFile(decl) };
const pattern = getPattern(decl);

if (!pattern) return;
if (!pattern) return Promise.resolve();

const promises = [];

decl.value = decl.value
.replace(pattern, (matched, before, url, after) => {
const newUrl = replaceUrl(url, dir, options, result, decl);
const newUrlPromise = replaceUrl(url, dir, options, result, decl);

promises.push(
newUrlPromise
.then((newUrl) => {
if (!newUrl) return matched;

if (!newUrl) return matched;
if (WITH_QUOTES.test(newUrl) && WITH_QUOTES.test(after)) {
before = before.slice(0, -1);
after = after.slice(1);
}

if (WITH_QUOTES.test(newUrl) && WITH_QUOTES.test(after)) {
before = before.slice(0, -1);
after = after.slice(1);
}
decl.value = decl.value.replace(matched, `${before}${newUrl}${after}`);
})
);

return `${before}${newUrl}${after}`;
return matched;
});

return Promise.all(promises);
};

module.exports = {
replaceUrl,
declProcessor
};

Expand Down
67 changes: 55 additions & 12 deletions src/lib/get-file.js
Expand Up @@ -5,31 +5,74 @@ const mime = require('mime');

const getPathByBasePath = require('./paths').getPathByBasePath;

const readFileAsync = (filePath) => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
});
});
};

const existFileAsync = (filePath) => {
return new Promise((resolve) =>
fs.access(filePath, (err) => {
resolve(!err);
})
);
};

const findExistsPath = (paths) => {
let resolved = false;

return new Promise((resolve, reject) => {
const findPromises = paths.map((path) => {
return existFileAsync(path).then((isExists) => {
if (!resolved && isExists) {
resolved = true;
resolve(path);
}
});
});

Promise.all(findPromises).then(() => {
if (!resolved) {
reject();
}
});
});
};

/**
*
* @param {PostcssUrl~Asset} asset
* @param {PostcssUrl~Options} options
* @param {PostcssUrl~Dir} dir
* @param {Function} warn
* @returns {PostcssUrl~File}
* @returns {Promise<PostcssUrl~File | Undefined>}
*/
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 findExistsPath(paths)
.then((path) => readFileAsync(path)
.then((contents) => {
return {
path,
contents,
mimeType: mime.getType(path)
};
})
)
.catch(() => {
warn(`Can't read file '${paths.join()}', ignoring`);

return {
path: filePath,
contents: fs.readFileSync(filePath),
mimeType: mime.getType(filePath)
};
return;
});
};

module.exports = getFile;
Expand Down
70 changes: 46 additions & 24 deletions src/type/copy.js
Expand Up @@ -14,8 +14,31 @@ 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);
+ calcHash(file.contents, options)
+ path.extname(file.path);

const createDirAsync = (dirPath) => {
return new Promise((resolve, reject) => {
mkdirp(dirPath, (err) => {
if (err) {
reject(err);
}

resolve();
});
});
};

const writeFileAsync = (file, dest) => {
return new Promise((resolve, reject) => {
fs.writeFile(dest, file.contents, { flag: 'wx' }, (err) => {
if (err) {
err.code === 'EEXIST' ? resolve() : reject(err);
}
resolve();
});
});
};

/**
* Copy images from readed from url() to an specific assets destination
Expand All @@ -33,37 +56,36 @@ const getHashName = (file, options) =>
* @param {Result} result
* @param {Function} addDependency
*
* @returns {String|Undefined}
* @returns {Promise<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;
return Promise.resolve();
}

const file = getFile(asset, options, dir, warn);

if (!file) return;

const assetRelativePath = options.useHash
? getHashName(file, options.hashOptions)
: asset.relativePath;
return getFile(asset, options, dir, warn)
.then((file) => {
if (!file) return;

const targetDir = getTargetDir(dir);
const newAssetBaseDir = getAssetsPath(targetDir, options.assetsPath);
const newAssetPath = path.join(newAssetBaseDir, assetRelativePath);
const newRelativeAssetPath = normalize(
path.relative(targetDir, newAssetPath)
);
const assetRelativePath = options.useHash
? getHashName(file, options.hashOptions)
: asset.relativePath;

mkdirp.sync(path.dirname(newAssetPath));

if (!fs.existsSync(newAssetPath)) {
fs.writeFileSync(newAssetPath, file.contents);
}
const targetDir = getTargetDir(dir);
const newAssetBaseDir = getAssetsPath(targetDir, options.assetsPath);
const newAssetPath = path.join(newAssetBaseDir, assetRelativePath);
const newRelativeAssetPath = normalize(path.relative(targetDir, newAssetPath));

addDependency(file.path);
return createDirAsync(path.dirname(newAssetPath))
.then(() => writeFileAsync(file, newAssetPath))
.then(() => {
addDependency(file.path);

return `${newRelativeAssetPath}${asset.search}${asset.hash}`;
return `${newRelativeAssetPath}${asset.search}${asset.hash}`;
});
}
);
};
4 changes: 2 additions & 2 deletions src/type/custom.js
Expand Up @@ -6,8 +6,8 @@
* @param {PostcssUrl~Dir} dir
* @param {PostcssUrl~Option} options
*
* @returns {String|Undefined}
* @returns {Promise<String|Undefined>}
*/
module.exports = function getCustomProcessor(asset, dir, options) {
return options.url.apply(null, arguments);
return Promise.resolve().then(() => options.url.apply(null, arguments));
};

0 comments on commit 99dde55

Please sign in to comment.