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

Refactor to improve types for validateTypes and whitespaceChecker #6002

Merged
merged 1 commit into from
Mar 31, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
101 changes: 101 additions & 0 deletions lib/utils/__tests__/validateTypes.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict';

const {
isBoolean,
isFunction,
isNullish,
isNumber,
isRegExp,
isString,
isPlainObject,
} = require('../validateTypes');

describe('isBoolean()', () => {
it('returns true when a boolean value is specified', () => {
expect(isBoolean(true)).toBe(true);
});

it('returns true when a Boolean object is specified', () => {
expect(isBoolean(new Boolean(true))).toBe(true);
});

it('returns false when a boolean value is not specified', () => {
expect(isBoolean(null)).toBe(false);
});
});

describe('isFunction()', () => {
it('returns true when a function value is specified', () => {
expect(isFunction(() => 1)).toBe(true);
});

it('returns true when a Function object is specified', () => {
expect(isFunction(new Function())).toBe(true);
});

it('returns false when a function value is specified', () => {
expect(isFunction(null)).toBe(false);
});
});

describe('isNullish()', () => {
it('returns true when null is specified', () => {
expect(isNullish(null)).toBe(true);
});

it('returns true when undefined is specified', () => {
expect(isNullish(undefined)).toBe(true);
});

it('returns false when neither null nor undefined is specified', () => {
expect(isNullish('')).toBe(false);
});
});

describe('isNumber()', () => {
it('returns true when a number value is specified', () => {
expect(isNumber(1)).toBe(true);
});

it('returns true when a Number object is specified', () => {
expect(isNumber(new Number(1))).toBe(true);
});

it('returns false when a number value is not specified', () => {
expect(isNumber(null)).toBe(false);
});
});

describe('isRegExp()', () => {
it('returns true when a regexp value is specified', () => {
expect(isRegExp(/a/)).toBe(true);
});

it('returns false when a regexp value is not specified', () => {
expect(isRegExp(null)).toBe(false);
});
});

describe('isString()', () => {
it('returns true when a string value is specified', () => {
expect(isString('')).toBe(true);
});

it('returns true when a String object is specified', () => {
expect(isString(new String(''))).toBe(true);
});

it('returns false when a string value is not specified', () => {
expect(isString(null)).toBe(false);
});
});

describe('isPlainObject()', () => {
it('returns true when a plain object is specified', () => {
expect(isPlainObject({})).toBe(true);
});

it('returns false when a plain object is not specified', () => {
expect(isPlainObject(null)).toBe(false);
});
});
34 changes: 33 additions & 1 deletion lib/utils/validateTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ function isBoolean(value) {
return typeof value === 'boolean' || value instanceof Boolean;
}

/**
* Checks if the value is a function or a Function object.
* @param {unknown} value
* @returns {value is Function}
*/
function isFunction(value) {
return typeof value === 'function' || value instanceof Function;
}

/**
* Checks if the value is *nullish*.
* @see https://developer.mozilla.org/en-US/docs/Glossary/Nullish
* @param {unknown} value
* @returns {value is null | undefined}
*/
function isNullish(value) {
return value == null;
}

/**
* Checks if the value is a number or a Number object.
* @param {unknown} value
Expand All @@ -21,7 +40,7 @@ function isNumber(value) {
}

/**
* Checks if the value is a RegExp object.
* Checks if the value is a regular expression.
* @param {unknown} value
* @returns {value is RegExp}
*/
Expand Down Expand Up @@ -57,6 +76,16 @@ function assert(value) {
console.assert(value);
}

/**
* Assert that the value is a function or a Function object.
* @param {unknown} value
* @returns {asserts value is Function}
*/
function assertFunction(value) {
// eslint-disable-next-line no-console
console.assert(isFunction(value), `"${value}" must be a function`);
}

/**
* Assert that the value is a number or a Number object.
* @param {unknown} value
Expand All @@ -79,12 +108,15 @@ function assertString(value) {

module.exports = {
isBoolean,
isFunction,
isNullish,
isNumber,
isRegExp,
isString,
isPlainObject,

assert,
assertFunction,
assertNumber,
assertString,
};
48 changes: 18 additions & 30 deletions lib/utils/whitespaceChecker.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const configurationError = require('./configurationError');
const isSingleLineString = require('./isSingleLineString');
const isWhitespace = require('./isWhitespace');
const { assertFunction, isNullish } = require('./validateTypes');

/**
* @typedef {(message: string) => string} MessageFunction
Expand Down Expand Up @@ -215,20 +216,20 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa
const oneCharBefore = source[index - 1];
const twoCharsBefore = source[index - 2];

if (!isValue(oneCharBefore)) {
if (isNullish(oneCharBefore)) {
return;
}

if (
targetWhitespace === 'space' &&
oneCharBefore === ' ' &&
(activeArgs.onlyOneChar || !isWhitespace(twoCharsBefore))
(activeArgs.onlyOneChar || isNullish(twoCharsBefore) || !isWhitespace(twoCharsBefore))
) {
return;
}

assertFunction(messageFunc);
activeArgs.err(messageFunc(activeArgs.errTarget || source[index]));
activeArgs.err(messageFunc(activeArgs.errTarget || source.charAt(index)));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note] source[index] can return undefined, while source.charAt(index) always returns a string. See the charAt() doc:

If index is out of range, charAt() returns an empty string.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt

I think it safe to use charAt() in this case to build an error message.

}

function expectBeforeAllowingIndentation(messageFunc = messages.expectedBefore) {
Expand All @@ -251,7 +252,7 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa
}

assertFunction(messageFunc);
err(messageFunc(activeArgs.errTarget || source[index]));
err(messageFunc(activeArgs.errTarget || source.charAt(index)));

return;
}
Expand All @@ -264,9 +265,9 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa

const oneCharBefore = source[index - 1];

if (isValue(oneCharBefore) && isWhitespace(oneCharBefore)) {
if (!isNullish(oneCharBefore) && isWhitespace(oneCharBefore)) {
assertFunction(messageFunc);
activeArgs.err(messageFunc(activeArgs.errTarget || source[index]));
activeArgs.err(messageFunc(activeArgs.errTarget || source.charAt(index)));
}
}

Expand All @@ -284,8 +285,9 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa

const oneCharAfter = source[index + 1];
const twoCharsAfter = source[index + 2];
const threeCharsAfter = source[index + 3];

if (!isValue(oneCharAfter)) {
if (isNullish(oneCharAfter)) {
return;
}

Expand All @@ -294,27 +296,30 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa
if (
oneCharAfter === '\r' &&
twoCharsAfter === '\n' &&
(activeArgs.onlyOneChar || !isWhitespace(source[index + 3]))
(activeArgs.onlyOneChar || isNullish(threeCharsAfter) || !isWhitespace(threeCharsAfter))
) {
return;
}

// If index is followed by a Unix LF ...
if (oneCharAfter === '\n' && (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter))) {
if (
oneCharAfter === '\n' &&
(activeArgs.onlyOneChar || isNullish(twoCharsAfter) || !isWhitespace(twoCharsAfter))
) {
return;
}
}

if (
targetWhitespace === 'space' &&
oneCharAfter === ' ' &&
(activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter))
(activeArgs.onlyOneChar || isNullish(twoCharsAfter) || !isWhitespace(twoCharsAfter))
) {
return;
}

assertFunction(messageFunc);
activeArgs.err(messageFunc(activeArgs.errTarget || source[index]));
activeArgs.err(messageFunc(activeArgs.errTarget || source.charAt(index)));
}

function rejectAfter(messageFunc = messages.rejectedAfter) {
Expand All @@ -324,9 +329,9 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa

const oneCharAfter = source[index + 1];

if (isValue(oneCharAfter) && isWhitespace(oneCharAfter)) {
if (!isNullish(oneCharAfter) && isWhitespace(oneCharAfter)) {
assertFunction(messageFunc);
activeArgs.err(messageFunc(activeArgs.errTarget || source[index]));
activeArgs.err(messageFunc(activeArgs.errTarget || source.charAt(index)));
}
}

Expand All @@ -337,20 +342,3 @@ module.exports = function whitespaceChecker(targetWhitespace, expectation, messa
afterOneOnly,
};
};

/**
* @param {unknown} x
*/
function isValue(x) {
return x !== undefined && x !== null;
}

/**
* @param {unknown} x
* @returns {asserts x is Function}
*/
function assertFunction(x) {
if (typeof x !== 'function') {
throw new TypeError(`\`${x}\` must be a function`);
}
}