-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(prettier-plugin-jsdoc): add fn to format access tag
- Loading branch information
Showing
6 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const TAGS_SYNONYMS = { | ||
virtual: 'abstract', | ||
extends: 'augments', | ||
constructor: 'class', | ||
const: 'constant', | ||
defaultvalue: 'default', | ||
desc: 'description', | ||
host: 'external', | ||
fileoverview: 'file', | ||
overview: 'file', | ||
emits: 'fires', | ||
func: 'function', | ||
method: 'function', | ||
var: 'member', | ||
arg: 'param', | ||
argument: 'param', | ||
prop: 'property', | ||
return: 'returns', | ||
exception: 'throws', | ||
yield: 'yields', | ||
}; | ||
|
||
module.exports.TAGS_SYNONYMS = TAGS_SYNONYMS; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
const R = require('ramda'); | ||
const { TAGS_SYNONYMS } = require('./constants'); | ||
/** | ||
* Replaces the tags synonyms for their "official" version. | ||
* | ||
* @param {CommentTag[]} tags The list of tags where the replacement should happen. | ||
* @returns {CommentTag[]} | ||
*/ | ||
const replaceTagsSynonyms = R.map((tag) => ({ | ||
...tag, | ||
tag: R.propOr(tag.tag, tag.tag, TAGS_SYNONYMS), | ||
})); | ||
/** | ||
* Ensures a given object is an array. | ||
* | ||
* @param {T|T[]} obj The object to validate. | ||
* @returns {T[]} | ||
* @template T | ||
*/ | ||
const ensureArray = R.unless(R.is(Array), R.of); | ||
/** | ||
* Creates a reducer that finds the last index of a tag on a list and saves it on the | ||
* accumulator using a custom property. | ||
* | ||
* @param {string|string[]} targetTag The name of the tag or tags the function should find. | ||
* @param {string} propName The name of the property that will be used for the | ||
* accumulator. | ||
* @returns {Object.<string,number>} | ||
*/ | ||
const findTagIndex = R.curry((targetTag, propName, step) => { | ||
const targetTags = ensureArray(targetTag); | ||
return (acc, tag, index) => { | ||
const nextAcc = targetTags.includes(tag.tag) ? | ||
R.assocPath([propName], index, acc) : | ||
acc; | ||
return step(nextAcc, tag, index); | ||
}; | ||
}); | ||
/** | ||
* Formats and normalizes the use of the access tag based on the plugin options. | ||
* | ||
* @param {CommentTag[]} tags The list of tags where the transformations should happen. | ||
* @param {PJPAccessTagOptions} options The plugin options for the access tag. | ||
* @return {CommentTag[] | ||
*/ | ||
const formatAccessTag = (tags, options) => { | ||
const indexes = tags.reduce( | ||
R.compose( | ||
findTagIndex('access', 'accessTag'), | ||
findTagIndex( | ||
['public', 'protected', 'private'], | ||
'typeTag', | ||
), | ||
)(R.identity), | ||
{ | ||
accessTag: -1, | ||
typeTag: -1, | ||
}, | ||
); | ||
|
||
let result; | ||
// If @access tags are allowed. | ||
if (options.jsdocAllowAccessTag) { | ||
// If @access tag needs to be enforced and there's a access type tag. | ||
if (options.jsdocEnforceAccessTag && indexes.typeTag > -1) { | ||
// If there's also an @access tag, just remove the access type tag. | ||
if (indexes.accessTag > -1) { | ||
result = R.remove(indexes.typeTag, 1, tags); | ||
indexes.typeTag = -1; | ||
} else { | ||
// If there's a access type tag but no @access tag, replace the access type tag with one. | ||
result = R.adjust(indexes.typeTag, (typeTag) => ({ | ||
tag: 'access', | ||
type: '', | ||
name: typeTag.tag, | ||
description: '', | ||
}), tags); | ||
} | ||
} | ||
// If @access tags are not allowed but there's one. | ||
} else if (indexes.accessTag > -1) { | ||
// If there's also an access type tag, just remove the @access tag. | ||
if (indexes.typeTag > -1) { | ||
result = R.remove(indexes.accessTag, 1, tags); | ||
indexes.accessTag = -1; | ||
} else { | ||
// If there's an @access tag but not access type tag, replace the @access tag with one. | ||
result = R.adjust(indexes.accessTag, (accessTag) => ({ | ||
tag: accessTag.name, | ||
type: '', | ||
name: '', | ||
description: '', | ||
}), tags); | ||
} | ||
} | ||
|
||
/** | ||
* If for some reason, there's an access tag and an access type tag and the options allow them | ||
* both, remove the first one declared. | ||
*/ | ||
if (indexes.accessTag > -1 && indexes.typeTag > -1) { | ||
const removeIndex = Math.min(indexes.accessTag, indexes.typeTag); | ||
result = R.remove(removeIndex, 1, tags); | ||
} | ||
|
||
/** | ||
* Return the modified object, if there's one, or just a clone of the tags list. The reason | ||
* for the short circuit is so the implementation can always asume that the method returns | ||
* a clone, no matter if no modification was made. | ||
*/ | ||
return result || R.clone(tags); | ||
}; | ||
|
||
module.exports.replaceTagsSynonyms = replaceTagsSynonyms; | ||
module.exports.formatAccessTag = formatAccessTag; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"root": true, | ||
"plugins": ["@homer0"], | ||
"extends": [ | ||
"plugin:@homer0/jest" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,213 @@ | ||
jest.unmock('../src/fns'); | ||
const { | ||
replaceTagsSynonyms, | ||
formatAccessTag, | ||
} = require('../src/fns'); | ||
|
||
describe('functions', () => { | ||
describe('replaceTagsSynonyms', () => { | ||
it('should replace tags synonyms', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'virtual', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'abstract', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let resultOne = null; | ||
let resultTwo = null; | ||
// When | ||
resultOne = replaceTagsSynonyms(input); | ||
resultTwo = replaceTagsSynonyms()(input); | ||
// Then | ||
expect(resultOne).toEqual(output); | ||
expect(resultTwo).toEqual(output); | ||
}); | ||
}); | ||
|
||
describe('formatAccessTag', () => { | ||
it('should replace "@access public" with "@public"', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'access', | ||
name: 'public', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'public', | ||
name: '', | ||
description: '', | ||
type: '', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: false, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
|
||
it('should only keep one type of "access" tag', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'access', | ||
name: 'public', | ||
}, | ||
{ | ||
tag: 'protected', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'protected', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: true, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
|
||
it('should replace "@private" with "@access private"', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'private', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'access', | ||
name: 'private', | ||
type: '', | ||
description: '', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: true, | ||
jsdocEnforceAccessTag: true, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
|
||
it('should remove "@protected" if there\'s "@access protected"', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'protected', | ||
}, | ||
{ | ||
tag: 'access', | ||
name: 'protected', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'access', | ||
name: 'protected', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: true, | ||
jsdocEnforceAccessTag: true, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
|
||
it('should remove "@access public" if there\'s "@public"', () => { | ||
// Given | ||
const input = [ | ||
{ | ||
tag: 'public', | ||
}, | ||
{ | ||
tag: 'access', | ||
name: 'public', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
const output = [ | ||
{ | ||
tag: 'public', | ||
}, | ||
{ | ||
tag: 'param', | ||
}, | ||
]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: false, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
|
||
it('shouldn\'t do anything if there are no "access tags"', () => { | ||
// Given | ||
const input = [{ | ||
tag: 'param', | ||
}]; | ||
const output = [{ | ||
tag: 'param', | ||
}]; | ||
let result = null; | ||
// When | ||
result = formatAccessTag(input, { | ||
jsdocAllowAccessTag: false, | ||
}); | ||
// Then | ||
expect(result).toEqual(output); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"typeAcquisition": { | ||
"include": ["jest"] | ||
} | ||
} |