Skip to content

Commit

Permalink
Merge pull request #953 from jorenbroekema/comment-formatting
Browse files Browse the repository at this point in the history
fix: account for multi-line comments in tokens, improve comment style
  • Loading branch information
jorenbroekema authored Oct 23, 2023
2 parents 9f2b2d2 + b9270b2 commit 18629d8
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 57 deletions.
183 changes: 149 additions & 34 deletions __tests__/common/formatHelpers/createPropertyFormatter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,39 +210,154 @@ const objectDictionary = createDictionary({
describe('common', () => {
describe('formatHelpers', () => {
describe('createPropertyFormatter', () => {
it('should support outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary, format: 'css' })
expect(propFormatter(dictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(dictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support outputReferences when values are transformed by (transitive) "value" transforms', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: transformedDictionary, format: 'css' })
expect(propFormatter(transformedDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(transformedDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support number values for outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: numberDictionary, format: 'css' })
expect(propFormatter(numberDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 10;');
expect(propFormatter(numberDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support multiple references for outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: multiDictionary, format: 'css' })
expect(propFormatter(multiDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 10px;');
expect(propFormatter(multiDictionary.tokens.tokens.bar)).toEqual(' --tokens-bar: 15px;');
expect(propFormatter(multiDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo) 5px var(--tokens-bar);');
})

it('should support object value references for outputReferences', () => {
// The ref is an object type value, which means there will usually be some kind of transform (e.g. a CSS shorthand transform)
// to change it from an object to a string. In our example, we use a border CSS shorthand for border token.
// In this case, since it is an object value, we will run the transformation on the transformed (string) value.
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: objectDictionary, format: 'css' })
expect(propFormatter(objectDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(objectDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo) dashed #FF00FF;');
})
describe('outputReferences', () => {
it('should support outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary, format: 'css' })
expect(propFormatter(dictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(dictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support outputReferences when values are transformed by (transitive) "value" transforms', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: transformedDictionary, format: 'css' })
expect(propFormatter(transformedDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(transformedDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support number values for outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: numberDictionary, format: 'css' })
expect(propFormatter(numberDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 10;');
expect(propFormatter(numberDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo);');
})

it('should support multiple references for outputReferences', () => {
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: multiDictionary, format: 'css' })
expect(propFormatter(multiDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 10px;');
expect(propFormatter(multiDictionary.tokens.tokens.bar)).toEqual(' --tokens-bar: 15px;');
expect(propFormatter(multiDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo) 5px var(--tokens-bar);');
})

it('should support object value references for outputReferences', () => {
// The ref is an object type value, which means there will usually be some kind of transform (e.g. a CSS shorthand transform)
// to change it from an object to a string. In our example, we use a border CSS shorthand for border token.
// In this case, since it is an object value, we will run the transformation on the transformed (string) value.
const propFormatter = createPropertyFormatter({ outputReferences: true, dictionary: objectDictionary, format: 'css' })
expect(propFormatter(objectDictionary.tokens.tokens.foo)).toEqual(' --tokens-foo: 5px;');
expect(propFormatter(objectDictionary.tokens.tokens.ref)).toEqual(' --tokens-ref: var(--tokens-foo) dashed #FF00FF;');
})
});

describe('commentStyle', () => {
const commentProperties = {
color: {
red: {
name: 'color-red',
value: '#FF0000',
comment: 'Foo bar qux',
attributes: {
category: 'color',
type: 'red',
},
path: ['color', 'red'],
},
blue: {
name: 'color-blue',
value: '#0000FF',
comment: 'Foo\nbar\nqux',
attributes: {
category: 'color',
type: 'blue',
},
path: ['color', 'blue'],
},
green: {
name: 'color-green',
value: '#00FF00',
comment: 'Foo bar qux',
attributes: {
category: 'color',
type: 'green',
},
path: ['color', 'green'],
},
},
};

const commentDictionary = createDictionary({
properties: commentProperties,
});

it('should default to putting comment next to the output value', () => {
// long commentStyle
const cssFormatter = createPropertyFormatter({
format: 'css',
commentDictionary,
});
// short commentStyle
const sassFormatter = createPropertyFormatter({
format: 'sass',
commentDictionary,
});

// red = single-line comment, blue = multi-line comment
const cssRed = cssFormatter(commentDictionary.tokens.color.red);
const cssBlue = cssFormatter(commentDictionary.tokens.color.blue);
const sassRed = sassFormatter(commentDictionary.tokens.color.red);
const sassBlue = sassFormatter(commentDictionary.tokens.color.blue);

// Note that since CSS puts it inside a selector, there is an indentation of 2 spaces as well
// CSS also has commentStyle long, whereas sass uses short
expect(cssRed).toMatchInlineSnapshot(
`" --color-red: #FF0000; /* Foo bar qux */"`
);

expect(cssBlue).toMatchInlineSnapshot(`
" /**
* Foo
* bar
* qux
*/
--color-blue: #0000FF;"
`);

expect(sassRed).toMatchInlineSnapshot(
`"$color-red: #FF0000; // Foo bar qux"`
);
expect(sassBlue).toMatchInlineSnapshot(`
"// Foo
// bar
// qux
$color-blue: #0000FF;"
`);
});

it('allows overriding formatting commentStyle', () => {
// long commentStyle
const cssFormatter = createPropertyFormatter({
format: 'css',
commentDictionary,
formatting: { commentStyle: 'long', commentPosition: 'above' },
});
// short commentStyle
const sassFormatter = createPropertyFormatter({
format: 'sass',
commentDictionary,
formatting: { commentStyle: 'short', commentPosition: 'above' },
});

const cssRed = cssFormatter(commentDictionary.tokens.color.green);
const sassRed = sassFormatter(commentDictionary.tokens.color.green);

expect(cssRed).toMatchInlineSnapshot(`
" /* Foo bar qux */
--color-green: #00FF00;"
`);

expect(sassRed).toMatchInlineSnapshot(`
"// Foo bar qux
$color-green: #00FF00;"
`);
});
});
})
})
})
})
102 changes: 79 additions & 23 deletions lib/common/formatHelpers/createPropertyFormatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,64 @@
const defaultFormatting = {
prefix: '',
commentStyle: 'long',
commentPosition: 'inline',
indentation: '',
separator: ' =',
suffix: ';'
};

/**
* Split a string comment by newlines and
* convert to multi-line comment if necessary
* @param {string} to_ret_prop
* @param {{comment: string; style: 'short' | 'long'; position: 'above' | 'inline'; indentation: string}} options
* @returns {string}
*/
function addComment(to_ret_prop, options) {
const { comment, style, indentation } = options;
let { position } = options;

const commentsByNewLine = comment.split("\n");
if (commentsByNewLine.length > 1) {
position = 'above';
}

let processedComment;
switch (style) {
case 'short':
if (position === 'inline') {
processedComment = `// ${comment}`;
} else {
processedComment = commentsByNewLine.reduce(
(acc, curr) => `${acc}${indentation}// ${curr}\n`,
''
);
// remove trailing newline
processedComment = processedComment.replace(/\n$/g, '');
}
break;
case 'long':
if (commentsByNewLine.length > 1) {
processedComment = commentsByNewLine.reduce(
(acc, curr) => `${acc}${indentation} * ${curr}\n`,
`${indentation}/**\n`
);
processedComment += `${indentation} */`;
} else {
processedComment = `${position === 'above' ? indentation : ''
}/* ${comment} */`;
}
break;
}

if (position === 'above') {
// put the comment above the prop if it's multi-line or if commentStyle ended with -above
to_ret_prop = `${processedComment}\n${to_ret_prop}`;
} else {
to_ret_prop = `${to_ret_prop} ${processedComment}`;
}

return to_ret_prop;
}

/**
Expand Down Expand Up @@ -57,33 +112,33 @@ function createPropertyFormatter({
formatting = {},
themeable = false
}) {
let {prefix, commentStyle, indentation, separator, suffix} = Object.assign({}, defaultFormatting, formatting);

switch(format) {
const formatDefaults = {};
switch (format) {
case 'css':
prefix = '--';
indentation = ' ';
separator = ':';
formatDefaults.prefix = '--';
formatDefaults.indentation = ' ';
formatDefaults.separator = ':';
break;
case 'sass':
prefix = '$';
commentStyle = 'short';
indentation = '';
separator = ':';
formatDefaults.prefix = '$';
formatDefaults.commentStyle = 'short';
formatDefaults.indentation = '';
formatDefaults.separator = ':';
break;
case 'less':
prefix = '@';
commentStyle = 'short';
indentation = '';
separator = ':';
formatDefaults.prefix = '@';
formatDefaults.commentStyle = 'short';
formatDefaults.indentation = '';
formatDefaults.separator = ':';
break;
case 'stylus':
prefix = '$';
commentStyle = 'short';
indentation = '';
separator = '=';
formatDefaults.prefix = '$';
formatDefaults.commentStyle = 'short';
formatDefaults.indentation = '';
formatDefaults.separator = '=';
break;
}
let {prefix, commentStyle, commentPosition, indentation, separator, suffix} = Object.assign({}, defaultFormatting, formatDefaults, formatting);

return function(prop) {
let to_ret_prop = `${indentation}${prefix}${prop.name}${separator} `;
Expand Down Expand Up @@ -152,11 +207,12 @@ function createPropertyFormatter({
to_ret_prop += suffix;

if (prop.comment && commentStyle !== 'none') {
if (commentStyle === 'short') {
to_ret_prop = to_ret_prop.concat(` // ${prop.comment}`);
} else {
to_ret_prop = to_ret_prop.concat(` /* ${prop.comment} */`);
}
to_ret_prop = addComment(to_ret_prop, {
comment: prop.comment,
style: commentStyle,
position: commentPosition,
indentation,
});
}

return to_ret_prop;
Expand Down

0 comments on commit 18629d8

Please sign in to comment.