Skip to content

Commit

Permalink
fix(prettier-plugin-jsdoc): make the function to prepare types work w…
Browse files Browse the repository at this point in the history
…ith tags
  • Loading branch information
homer0 committed Oct 13, 2020
1 parent 4011836 commit 2c4b0cd
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 57 deletions.
42 changes: 0 additions & 42 deletions src/fns/preparePrettyType.js

This file was deleted.

80 changes: 80 additions & 0 deletions src/fns/prepareTagPrettyType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
const { format } = require('prettier');
const R = require('ramda');
const { isMatch } = require('./utils');

/**
* @typedef {import('../types').PrettierOptions} PrettierOptions
* @typedef {import('../types').CommentTag} CommentTag
*/

/**
* Checks whether a tag type uses "complex" features, like generics, union, intercection, etc.
*
* @callback HasComplexTypeFn
* @param {CommentTag} tag The tag which type will be validated.
* @returns {boolean}
*/

/**
* @type {HasComplexTypeFn}
*/
const hasComplexType = R.compose(
isMatch(/[&<\.\|]/),
R.prop('type'),
);

/**
* Takes the type of a tag, creates some fake TS code, sends it to Prettier, cleans the result
* and sets it up as the new type of the tag.
*
* @callback PreparePrettyTypeFn
* @param {PrettierOptions} options The options sent to the plugin.
* @param {CommentTag} tag The tag which type will be formatted.
* @returns {CommentTag}
*/

/**
* @type {PreparePrettyTypeFn}
*/
const preparePrettyType = R.curry((options, tag) => {
let result;
try {
const useType = tag.type.replace(/\*/g, 'any');
const prefix = 'type complex = ';
const newType = format(`${prefix}${useType}`, {
...options,
parser: 'typescript',
})
.substr(prefix.length)
.trim()
.replace(/;$/, '');
result = R.assocPath(['type'], newType, tag);
} catch (ignore) {
// Ignore the error because if it failed, it's an issue with Pettier.
result = R.clone(tag);
}

return result;
});

/**
* Checks if tag type uses generics/union/intersections/properties in order to format it using
* Prettier as if it were code.
*
* @callback PrepareTagPrettyType
* @param {CommentTag} tag The tag to validate and format.
* @param {PrettierOptions} options The options that were sent to the plugin, to send back to
* Prettier.
* @returns {CommentTag}
*/

/**
* @type {PrepareTagPrettyType}
*/
const prepareTagPrettyType = R.curry((tag, options) => R.when(
hasComplexType,
preparePrettyType(options),
tag,
));

module.exports.prepareTagPrettyType = prepareTagPrettyType;
Original file line number Diff line number Diff line change
@@ -1,42 +1,52 @@
jest.unmock('../../src/fns/preparePrettyType');
jest.unmock('../../src/fns/prepareTagPrettyType');
jest.unmock('../../src/fns/utils');
jest.mock('prettier');

const { format } = require('prettier');
const { preparePrettyType } = require('../../src/fns/preparePrettyType');
const { prepareTagPrettyType } = require('../../src/fns/prepareTagPrettyType');

describe('preparePrettyType', () => {
describe('prepareTagPrettyType', () => {
beforeEach(() => {
format.mockClear();
});

it('should ignore a basic type', () => {
// Given
const input = 'string';
const output = 'string';
const input = {
type: 'string',
};
const output = {
type: 'string',
};
let result = null;
// When
result = preparePrettyType(input, {});
result = prepareTagPrettyType(input, {});
// Then
expect(result).toBe(output);
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(0);
});

it('should call prettier for a complex type', () => {
// Given
const prettierResponse = 'prettier-response';
format.mockImplementationOnce((code) => code.replace(/=.*?$/, `= ${prettierResponse};`));
const input = 'React.FC<string>';
const input = {
type: 'React.FC<string>',
};
const output = {
type: prettierResponse,
};
const options = {
semi: true,
indent: 2,
};
let result = null;
// When
result = preparePrettyType(input, options);
result = prepareTagPrettyType(input, options);
// Then
expect(result).toBe(prettierResponse);
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(1);
expect(format).toHaveBeenCalledWith(`type complex = ${input}`, {
expect(format).toHaveBeenCalledWith(`type complex = ${input.type}`, {
...options,
parser: 'typescript',
});
Expand All @@ -47,17 +57,21 @@ describe('preparePrettyType', () => {
format.mockImplementationOnce(() => {
throw new Error();
});
const input = 'React.FC<string>';
const output = 'React.FC<string>';
const input = {
type: 'React.FC<string>',
};
const output = {
type: 'React.FC<string>',
};
const options = {
semi: true,
indent: 2,
};
let result = null;
// When
result = preparePrettyType(input, options);
result = prepareTagPrettyType(input, options);
// Then
expect(result).toBe(output);
expect(result).toEqual(output);
expect(format).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 2c4b0cd

Please sign in to comment.