Skip to content

Commit

Permalink
Release/1.0.0 beta.1 (#200)
Browse files Browse the repository at this point in the history
* feat(npm): add additional eslint plugins

* feat: convert promise functions to async/await

* feat: add additional arrow icon to demo test

* test: update reference sprite layouts for tests

* feat: remove lodash functions with native es6 ones

* chore(npm): update husky to v3.03

* feat(jsdoc): install jsdoc library and npm script
  • Loading branch information
patrickcate authored Aug 10, 2019
1 parent 8cda92f commit f125a5b
Show file tree
Hide file tree
Showing 42 changed files with 631 additions and 333 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
commitizen.config.js
commitlint.config.js
coverage
docs
12 changes: 10 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ module.exports = {
parserOptions: {
ecmaVersion: 2017,
},
plugins: ['prettier', 'jsdoc'],
extends: ['eslint:recommended', 'airbnb-base', 'plugin:prettier/recommended'],
plugins: ['prettier', 'jsdoc', 'promise', 'sonarjs'],
extends: [
'plugin:promise/recommended',
'plugin:sonarjs/recommended',
'eslint:recommended',
'airbnb-base',
'plugin:prettier/recommended',
],
rules: {
// Only allow debugger in development
'no-debugger': process.env.PRE_COMMIT ? 'error' : 'off',
Expand Down Expand Up @@ -59,5 +65,7 @@ module.exports = {
'jsdoc/require-returns-description': 'off',
'jsdoc/require-returns-type': 'warn',
'jsdoc/valid-types': 'warn',
'promise/prefer-await-to-then': 'warn',
'promise/prefer-await-to-callbacks': 'warn',
},
};
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/node_modules/**
**/output.css
docs
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Projects with a large number of sprites can take a long time to compile. The hel

## Relative/absolute paths

The plugin supports both relative andabsolute paths on input file, but can currently only [generate relative paths on output file](https://github.com/glebmachine/postcss-easysprites/issues/4).
The plugin supports both relative and absolute paths on input file, but can currently only [generate relative paths on output file](https://github.com/glebmachine/postcss-easysprites/issues/4).

## Plugin options

Expand Down
1 change: 0 additions & 1 deletion commitizen.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const scopes = [
'gulp',
'jest',
'jsdoc',
'lodash',
'mocha',
'nodejs',
'npm',
Expand Down
35 changes: 12 additions & 23 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,20 @@ const { mapSpritesProperties, saveSprites } = require('./lib/sprites');
* @param {processOptions} [options] Options passed to the plugin.
*/
module.exports = postcss.plugin('postcss-easysprites', (options) => {
return (css) => {
return async (css) => {
// Setup options.
pluginOptions.init(options, css.source.input.file);

return Promise.all([collectImages(css)])
.then(([images]) => {
return addSpriteGroups(images);
})
.then(([images]) => {
return setTokens(images, css);
})
.then(([images]) => {
return runSpritesmith(images);
})
.then(([images, sprites]) => {
return saveSprites(images, sprites);
})
.then(([images, sprites]) => {
return mapSpritesProperties(images, sprites);
})
.then(([images, sprites]) => {
return updateReferences(images, sprites, css);
})
.catch((err) => {
throw new Error(err);
});
try {
const images = await collectImages(css);
await addSpriteGroups(images);
await setTokens(images, css);
const sprites = await runSpritesmith(images);
await saveSprites(images, sprites);
await mapSpritesProperties(images, sprites);
await updateReferences(images, sprites, css);
} catch (error) {
throw new Error(error);
}
};
});
25 changes: 25 additions & 0 deletions jsdoc.conf.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"plugins": [],
"recurseDepth": 10,
"source": {
"include": ["lib"],
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"]
},
"templates": {
"cleverLinks": false,
"monospaceLinks": false
},
"opts": {
"template": "templates/default",
"encoding": "utf8",
"destination": "./docs/",
"recurse": true,
"readme": "README.md"
}
}
24 changes: 20 additions & 4 deletions lib/cache.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const fs = require('fs');
const md5 = require('md5');
const { promisify } = require('util');

const readFileAsync = promisify(fs.readFile);

/**
* Custom module to cache sprite images so unchanged sprites don't need to be
* processed by Spritesmith.
Expand All @@ -8,12 +12,24 @@ const cache = {
cache: {},
cacheIndex: {},

createCacheHash(imagePaths) {
const cacheHashes = imagePaths.map((imagePath) => {
return `${imagePath}=${md5(fs.readFileSync(imagePath).toString())}`;
async createCacheHash(imagePaths) {
const allPromisedCacheHashes = imagePaths.map(async (imagePath) => {
try {
const image = await readFileAsync(imagePath);
return `${imagePath}=${md5(image.toString())}`;
} catch (error) {
return undefined;
}
});

return md5(cacheHashes.sort().join('&'));
const cacheHashes = await Promise.all(allPromisedCacheHashes);

const hashString = cacheHashes
.filter((cacheHash) => cacheHash)
.sort()
.join('&');

return hashString ? md5(hashString) : undefined;
},
getCache() {
return this.cache;
Expand Down
104 changes: 51 additions & 53 deletions lib/collect-images.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
const fs = require('fs');
const { promisify } = require('util');
const ansi = require('ansi-colors');
const lodash = require('lodash');
const url = require('url');

const fsAccessAsync = promisify(fs.access);
const { log } = require('./log');
const { isRetinaImage, getRetinaRatio } = require('./retina-images');
const { pluginOptions } = require('./plugin-options');

const {
isLocalUrl,
hasImageInRule,
Expand Down Expand Up @@ -66,46 +66,42 @@ function buildImageData(cssRule) {
* @returns {boolean}
*/
async function fileExistsAsync(path) {
const fsAccessAsync = promisify(fs.access);

const fileExists = await fsAccessAsync(path)
.then(() => true)
.catch(() => false);

return fileExists;
try {
await fsAccessAsync(path);
return true;
} catch (error) {
return false;
}
}

/**
* Checks if a image actually exists.
*
* @param {object} image - Object of image properties.
* @param {Array} ruleNodes - The css rule nodes.
* @param {Array} images - The array of images that exist.
* @param {Array} rules - The css rule nodes.
* @returns {Promise}
*/
function imageExists(image, ruleNodes, images) {
return fileExistsAsync(image.path)
.then((result) => {
if (result) {
images.push(image);
} else {
log(
'Easysprites:',
ansi.red(image.path),
'file unreachable or does not exists'
);

// If the image file doesn't exist, clean up the background image URL's
// by removing hash suffix.
ruleNodes.forEach((node) => {
const nodeRule = node;
nodeRule.value = node.value.replace(`#${image.hash}`, '');
});
}
})
.catch((error) => {
throw new Error(error);
});
async function imageExists(image, rules) {
const exists = await fileExistsAsync(image.path);

if (exists) {
return image;
}

log(
'Easysprites:',
ansi.red(image.path),
'file unreachable or does not exists'
);

// If the image file doesn't exist, clean up the background image URL's
// by removing hash suffix.
rules.forEach((node) => {
const nodeRule = node;
nodeRule.value = node.value.replace(`#${image.hash}`, '');
});

return undefined;
}

/**
Expand All @@ -114,31 +110,33 @@ function imageExists(image, ruleNodes, images) {
* @param {object} css - Object with CSS results.
* @returns {Array}
*/
function collectImages(css) {
async function collectImages(css) {
const images = [];
let allImagesChecked;

return new Promise((resolve) => {
// Loop through all CSS rules.
css.walkRules((rule) => {
// Get the string representation of the PostCSS rule object.
const cssRule = rule.toString();
// Loop through all CSS rules.
css.walkRules((rule) => {
// Get the string representation of the PostCSS rule object.
const cssRule = rule.toString();

// Check if there is a `background(-image)` rule with a url() defined.
if (!hasImageInRule(cssRule)) return;
// Check if there is a `background(-image)` rule with a url() defined.
if (!hasImageInRule(cssRule)) return;

const image = buildImageData(cssRule);
if (!image) return;
const image = buildImageData(cssRule);
if (!image) return;

allImagesChecked = imageExists(image, rule.nodes, images);
});

return allImagesChecked
? allImagesChecked.then(() => {
resolve(lodash.uniqWith(images, lodash.isEqual));
})
: resolve(images);
images.push(imageExists(image, rule.nodes));
});

const allFoundImages = await Promise.all(images).then((array) =>
array.filter((image) => !!image)
);

// Return an array with all duplicates removed.
return [
...new Map(
allFoundImages.map((obj) => [JSON.stringify(obj), obj])
).values(),
];
}

exports.collectImages = collectImages;
Expand Down
4 changes: 1 addition & 3 deletions lib/retina-images.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ function getRetinaRatio(imageUrl) {
const matches = retinaPattern.exec(imageUrl.split('#')[0]);

// Convert the string number to an actual integer.
const ratio = parseInt(matches[1], 10);

return ratio;
return parseInt(matches[1], 10);
}

/**
Expand Down
Loading

0 comments on commit f125a5b

Please sign in to comment.