Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add registerFilter method #233

Merged
merged 7 commits into from
Jan 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions __tests__/filterProperties.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,21 @@ describe('filterProperties', () => {
expect(filteredDictionary.properties).not.toHaveProperty('color');
});

it('should work with a filter object', () => {
var filter = { "attributes": { "category": "size" } };
var filteredDictionary = filterProperties(dictionary, filter);
_.each(filteredDictionary.allProperties, function(property) {
expect(property).not.toBe(colorRed);
expect(property).not.toBe(colorBlue);
describe('should throw if', () => {
it('filter is a string', () => {
expect(
function(){
filterProperties(dictionary, 'my_filter')
}
).toThrow(/filter is not a function/);
});
it('filter is an object', () => {
expect(
function(){
filterProperties(dictionary, { "attributes": { "category": "size" } })
}
).toThrow(/filter is not a function/);
});
expect(filteredDictionary.allProperties).toEqual([sizeSmall, sizeLarge]);
expect(filteredDictionary.properties).toHaveProperty('size');
expect(filteredDictionary.properties).not.toHaveProperty('color');

});

});
29 changes: 28 additions & 1 deletion docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,34 @@ StyleDictionary.registerAction({

* * *

### registerFormat
### registerFilter
> StyleDictionary.registerFilter(filter) ⇒ [<code>style-dictionary</code>](#module_style-dictionary)




Add a custom filter to the style dictionary


| Param | Type | Description |
| --- | --- | --- |
| filter | <code>Object</code> | |
| filter.name | <code>String</code> | Name of the filter to be referenced in your config.json |
| filter.matcher | <code>function</code> | Matcher function, return boolean if filter should be applied. |

**Example**
```js
StyleDictionary.registerFilter({
name: 'isColor',
matcher: function(prop) {
return return prop.attributes.category === 'color';
}
})
```

* * *

### registerFormat
> StyleDictionary.registerFormat(format) ⇒ [<code>style-dictionary</code>](#module_style-dictionary)


Expand Down
6 changes: 3 additions & 3 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ By default, Style Dictionary looks for a `config.json` file in the root of your
| source | Array[String] | An array of paths to JSON files that contain style properties. The Style Dictionary will do a deep merge of all of the JSON files so you can separate your properties into multiple files. |
| platforms | Object | An object containing platform config objects that describe how the Style Dictionary should build for that platform. You can add any arbitrary attributes on this object that will get passed to formats and actions (more on these in a bit). This is useful for things like build paths, name prefixes, variable names, etc. |
| platform.transforms | Array[String] (optional) | An array of [transforms](transforms.md) to be performed on the style properties object. These will transform the properties in a non-destructive way so each platform can transform the properties. Transforms to apply sequentially to all properties. Can be a built-in one or you can create your own. |
| platform.transformGroup | String (optional) | A string that maps to an array of transforms. This makes it easier to reference transforms by grouping them together. You must either define this or `transforms`. |
| platform.transformGroup | String (optional) | A string that maps to an array of transforms. This makes it easier to reference transforms by grouping them together. You must either define this or [transforms](transforms.md). |
| platform.buildPath | String (optional) | Base path to build the files, must end with a trailing slash. |
| platform.files | Array (optional) | Files to be generated for this platform. |
| platform.file.destination | String (optional) | Location to build the file, will be appended to the buildPath. |
| platform.file.format | String (optional) | [Format](formats.md) used to generate the file. Can be a built-in one or you can create your own. |
| platform.file.filter | Function/Object (optional) | A function or object used to filter the properties that will be included in the file. If a function is provided, each property will be passed to the function and the result (true or false) will determine whether the property is included. If an object is provided, each property will be matched against the object using a partial deep comparison. If a match is found, the property is included. |
| platform.file.format | String (optional) | [Format](formats.md) used to generate the file. Can be a built-in one or you can create your own via [registerFormat](api.md#registerformat). |
| platform.file.filter | String/Function/Object (optional) | A function, string or object used to filter the properties that will be included in the file. If a function is provided, each property will be passed to the function and the result (true or false) will determine whether the property is included. If an object is provided, each property will be matched against the object using a partial deep comparison. If a match is found, the property is included. If a string is passed, is considered a custom filter registered via [registerFilter](api.md#registerfilter) |
| platform.file.options | Object (optional) | A set of extra options associated with the file. Only includes 'showFileHeader' at this time. |
| platform.file.options.showFileHeader | Boolean | If the generated file should have a "Do not edit + Timestamp" header (where the format supports it). By default is "true". |
| platform.actions | Array[String] (optional) | [Actions](actions.md) to be performed after the files are built for that platform. Actions can be any arbitrary code you want to run like copying files, generating assets, etc. You can use pre-defined actions or create custom actions. |
Expand Down
1 change: 1 addition & 0 deletions docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The style dictionary build system is easily extended. We don't know exactly how
## Extension Functions in the API
* [registerTransform](api.md#registertransform)
* [registerTransformGroup](api.md#registertransformgroup)
* [registerFilter](api.md#registerfilter)
* [registerFormat](api.md#registerformat)
* [registerTemplate](api.md#registertemplate)
* [registerAction](api.md#registeraction)
Expand Down
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ var StyleDictionary = {
transformGroup: require('./lib/common/transformGroups'),
format: require('./lib/common/formats'),
action: require('./lib/common/actions'),
filter: {}, // we need to initialise the object, since we don't have built-in filters

registerTransform: require('./lib/register/transform'),
registerTransformGroup: require('./lib/register/transformGroup'),
registerFormat: require('./lib/register/format'),
registerTemplate: require('./lib/register/template'),
registerAction: require('./lib/register/action'),
registerFilter: require('./lib/register/filter'),

exportPlatform: require('./lib/exportPlatform'),
buildPlatform: require('./lib/buildPlatform'),
Expand Down
12 changes: 5 additions & 7 deletions lib/filterProperties.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ function filterPropertyArray(properties, filter) {
* object using a function provided by the user.
*
* @param {Object} dictionary
* @param {Function|Object} filter - Either a function that receives a property
* object and returns `true` if the property should be included in the output
* or `false` if the property should be excluded from the output, or an object
* to compare each property to (see lodash/matches).
* @param {Function} filter - A function that receives a property object
* and returns `true` if the property should be included in the output
* or `false` if the property should be excluded from the output
* @returns {Object} dictionary - A new dictionary containing only the
* properties that matched the filter (or the original dictionary if no filter
* function was provided).
Expand All @@ -82,10 +81,9 @@ function filterProperties(dictionary, filter) {
if (!filter) {
return dictionary
} else {
const filterFunc = _.isFunction(filter) ? filter : _.matches(filter)
return _.assign({}, dictionary, {
allProperties: filterPropertyArray(dictionary.allProperties, filterFunc),
properties: filterPropertyObject(dictionary.properties, filterFunc)
allProperties: filterPropertyArray(dictionary.allProperties, filter),
properties: filterPropertyObject(dictionary.properties, filter)
})
}
}
Expand Down
43 changes: 43 additions & 0 deletions lib/register/filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/

/**
* Add a custom format to the style dictionary
* @static
* @memberof module:style-dictionary
* @param {Object} filter
* @param {String} filter.name - Name of the filter to be referenced in your config.json
* @param {Function} filter.matcher - Matcher function, return boolean if the property should be included.
* @returns {module:style-dictionary}
* @example
* ```js
* StyleDictionary.registerFilter({
* name: 'isColor',
* matcher: function(prop) {
* return prop.attributes.category === 'color';
* }
* })
* ```
*/
function registerFilter(options) {
if (typeof options.name !== 'string')
throw new Error('Can\'t register filter; filter.name must be a string');
if (typeof options.matcher !== 'function')
throw new Error('Can\'t register filter; filter.matcher must be a function');

this.filter[options.name] = options.matcher;

return this;
}

module.exports = registerFilter;
29 changes: 23 additions & 6 deletions lib/transform/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
*/

var _ = require('lodash'),
chalk = require('chalk'),
GroupMessages = require('../utils/groupMessages');

var TEMPLATE_DEPRECATION_WARNINGS = GroupMessages.GROUP.TemplateDeprecationWarnings;

/**
* Takes a platform config object and returns a new one
* that has transforms, formats, and actions
* that has filters, transforms, formats, and actions
* mapped properly.
* @private
* @param {Object} config
Expand All @@ -38,8 +37,9 @@ function transformConfig(config, dictionary) {
if (to_ret.transforms) {
transforms = to_ret.transforms;
} else if (to_ret.transformGroup) {
transforms = dictionary.transformGroup[to_ret.transformGroup];
if (!transforms) {
if (dictionary.transformGroup[to_ret.transformGroup]) {
transforms = dictionary.transformGroup[to_ret.transformGroup];
} else {
throw new Error('transformGroup ' + to_ret.transformGroup + ' doesn\'t exist');
}
}
Expand All @@ -52,25 +52,42 @@ function transformConfig(config, dictionary) {
});

to_ret.files = _.map(config.files, function(file) {
const ext = {};
if (file.filter) {
if(typeof file.filter === 'string') {
if (dictionary.filter[file.filter]) {
ext.filter = dictionary.filter[file.filter];
} else {
throw new Error('Can\'t find filter: ' + file.filter);
}
} else if (typeof file.filter === 'object') {
ext.filter = _.matches(file.filter);
} else if (typeof file.filter === 'function') {
ext.filter = file.filter;
} else {
throw new Error('Filter format not valid: ' + typeof file.filter);
}
}
if (file.template) {
if (dictionary.format[file.template]) {
GroupMessages.add(
TEMPLATE_DEPRECATION_WARNINGS,
`${file.destination} (template: ${file.template})`
);
return _.extend({}, file, {format: dictionary.format[file.template]});
ext.format = dictionary.format[file.template];
} else {
throw new Error('Can\'t find template: ' + file.template);
}
} else if (file.format) {
if (dictionary.format[file.format]) {
return _.extend({}, file, {format: dictionary.format[file.format]});
ext.format = dictionary.format[file.format];
} else {
throw new Error('Can\'t find format: ' + file.format);
}
} else {
throw new Error('Please supply a format for file: ' + JSON.stringify(file));
}
return _.extend({}, file, ext);
});

to_ret.actions = _.map(config.actions, function(action) {
Expand Down