blob: 677010f92c003bf985b63382952d31d49e513186 [file] [log] [blame]
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const util = __importStar(require("../util"));
const util_1 = require("../util");
const definition = {
type: 'object',
properties: {
before: { type: 'boolean' },
after: { type: 'boolean' },
},
additionalProperties: false,
};
function createRules(options) {
var _a;
const globals = Object.assign(Object.assign({}, ((options === null || options === void 0 ? void 0 : options.before) !== undefined ? { before: options.before } : {})), ((options === null || options === void 0 ? void 0 : options.after) !== undefined ? { after: options.after } : {}));
const override = (_a = options === null || options === void 0 ? void 0 : options.overrides) !== null && _a !== void 0 ? _a : {};
const colon = Object.assign(Object.assign({ before: false, after: true }, globals), override === null || override === void 0 ? void 0 : override.colon);
const arrow = Object.assign(Object.assign({ before: true, after: true }, globals), override === null || override === void 0 ? void 0 : override.arrow);
return {
colon: colon,
arrow: arrow,
variable: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.variable),
property: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.property),
parameter: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.parameter),
returnType: Object.assign(Object.assign({}, colon), override === null || override === void 0 ? void 0 : override.returnType),
};
}
function getIdentifierRules(rules, node) {
const scope = node === null || node === void 0 ? void 0 : node.parent;
if ((0, util_1.isVariableDeclarator)(scope)) {
return rules.variable;
}
else if ((0, util_1.isFunctionOrFunctionType)(scope)) {
return rules.parameter;
}
else {
return rules.colon;
}
}
function getRules(rules, node) {
var _a;
const scope = (_a = node === null || node === void 0 ? void 0 : node.parent) === null || _a === void 0 ? void 0 : _a.parent;
if ((0, util_1.isTSFunctionType)(scope) || (0, util_1.isTSConstructorType)(scope)) {
return rules.arrow;
}
else if ((0, util_1.isIdentifier)(scope)) {
return getIdentifierRules(rules, scope);
}
else if ((0, util_1.isClassOrTypeElement)(scope)) {
return rules.property;
}
else if ((0, util_1.isFunction)(scope)) {
return rules.returnType;
}
else {
return rules.colon;
}
}
exports.default = util.createRule({
name: 'type-annotation-spacing',
meta: {
type: 'layout',
docs: {
description: 'Require consistent spacing around type annotations',
recommended: false,
},
fixable: 'whitespace',
messages: {
expectedSpaceAfter: "Expected a space after the '{{type}}'.",
expectedSpaceBefore: "Expected a space before the '{{type}}'.",
unexpectedSpaceAfter: "Unexpected space after the '{{type}}'.",
unexpectedSpaceBefore: "Unexpected space before the '{{type}}'.",
unexpectedSpaceBetween: "Unexpected space between the '{{previousToken}}' and the '{{type}}'.",
},
schema: [
{
type: 'object',
properties: {
before: { type: 'boolean' },
after: { type: 'boolean' },
overrides: {
type: 'object',
properties: {
colon: definition,
arrow: definition,
variable: definition,
parameter: definition,
property: definition,
returnType: definition,
},
additionalProperties: false,
},
},
additionalProperties: false,
},
],
},
defaultOptions: [
// technically there is a default, but the overrides mean
// that if we apply them here, it will break the no override case.
{},
],
create(context, [options]) {
const punctuators = [':', '=>'];
const sourceCode = context.getSourceCode();
const ruleSet = createRules(options);
/**
* Checks if there's proper spacing around type annotations (no space
* before colon, one space after).
*/
function checkTypeAnnotationSpacing(typeAnnotation) {
const nextToken = typeAnnotation;
const punctuatorTokenEnd = sourceCode.getTokenBefore(nextToken);
let punctuatorTokenStart = punctuatorTokenEnd;
let previousToken = sourceCode.getTokenBefore(punctuatorTokenEnd);
let type = punctuatorTokenEnd.value;
if (!punctuators.includes(type)) {
return;
}
const { before, after } = getRules(ruleSet, typeAnnotation);
if (type === ':' && previousToken.value === '?') {
if (sourceCode.isSpaceBetweenTokens(previousToken, punctuatorTokenStart)) {
context.report({
node: punctuatorTokenStart,
messageId: 'unexpectedSpaceBetween',
data: {
type,
previousToken: previousToken.value,
},
fix(fixer) {
return fixer.removeRange([
previousToken.range[1],
punctuatorTokenStart.range[0],
]);
},
});
}
// shift the start to the ?
type = '?:';
punctuatorTokenStart = previousToken;
previousToken = sourceCode.getTokenBefore(previousToken);
// handle the +/- modifiers for optional modification operators
if (previousToken.value === '+' || previousToken.value === '-') {
type = `${previousToken.value}?:`;
punctuatorTokenStart = previousToken;
previousToken = sourceCode.getTokenBefore(previousToken);
}
}
const previousDelta = punctuatorTokenStart.range[0] - previousToken.range[1];
const nextDelta = nextToken.range[0] - punctuatorTokenEnd.range[1];
if (after && nextDelta === 0) {
context.report({
node: punctuatorTokenEnd,
messageId: 'expectedSpaceAfter',
data: {
type,
},
fix(fixer) {
return fixer.insertTextAfter(punctuatorTokenEnd, ' ');
},
});
}
else if (!after && nextDelta > 0) {
context.report({
node: punctuatorTokenEnd,
messageId: 'unexpectedSpaceAfter',
data: {
type,
},
fix(fixer) {
return fixer.removeRange([
punctuatorTokenEnd.range[1],
nextToken.range[0],
]);
},
});
}
if (before && previousDelta === 0) {
context.report({
node: punctuatorTokenStart,
messageId: 'expectedSpaceBefore',
data: {
type,
},
fix(fixer) {
return fixer.insertTextAfter(previousToken, ' ');
},
});
}
else if (!before && previousDelta > 0) {
context.report({
node: punctuatorTokenStart,
messageId: 'unexpectedSpaceBefore',
data: {
type,
},
fix(fixer) {
return fixer.removeRange([
previousToken.range[1],
punctuatorTokenStart.range[0],
]);
},
});
}
}
return {
TSMappedType(node) {
if (node.typeAnnotation) {
checkTypeAnnotationSpacing(node.typeAnnotation);
}
},
TSTypeAnnotation(node) {
checkTypeAnnotationSpacing(node.typeAnnotation);
},
};
},
});
//# sourceMappingURL=type-annotation-spacing.js.map