blob: fda429208b941e380a7103cd1127af7f5e390d9c [file] [log] [blame]
"use strict";
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = 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, _b, _c, _d, _e, _f, _g, _h, _j, _k;
const globals = Object.assign(Object.assign({}, (((_a = options) === null || _a === void 0 ? void 0 : _a.before) !== undefined ? { before: options.before } : {})), (((_b = options) === null || _b === void 0 ? void 0 : _b.after) !== undefined ? { after: options.after } : {}));
const override = (_d = (_c = options) === null || _c === void 0 ? void 0 : _c.overrides, (_d !== null && _d !== void 0 ? _d : {}));
const colon = Object.assign(Object.assign({ before: false, after: true }, globals), (_e = override) === null || _e === void 0 ? void 0 : _e.colon);
const arrow = Object.assign(Object.assign({ before: true, after: true }, globals), (_f = override) === null || _f === void 0 ? void 0 : _f.arrow);
return {
colon: colon,
arrow: arrow,
variable: Object.assign(Object.assign({}, colon), (_g = override) === null || _g === void 0 ? void 0 : _g.variable),
property: Object.assign(Object.assign({}, colon), (_h = override) === null || _h === void 0 ? void 0 : _h.property),
parameter: Object.assign(Object.assign({}, colon), (_j = override) === null || _j === void 0 ? void 0 : _j.parameter),
returnType: Object.assign(Object.assign({}, colon), (_k = override) === null || _k === void 0 ? void 0 : _k.returnType),
};
}
function getIdentifierRules(rules, node) {
var _a;
const scope = (_a = node) === null || _a === void 0 ? void 0 : _a.parent;
if (util_1.isVariableDeclarator(scope)) {
return rules.variable;
}
else if (util_1.isFunctionOrFunctionType(scope)) {
return rules.parameter;
}
else {
return rules.colon;
}
}
function getRules(rules, node) {
var _a, _b;
const scope = (_b = (_a = node) === null || _a === void 0 ? void 0 : _a.parent) === null || _b === void 0 ? void 0 : _b.parent;
if (util_1.isTSFunctionType(scope)) {
return rules.arrow;
}
else if (util_1.isIdentifier(scope)) {
return getIdentifierRules(rules, scope);
}
else if (util_1.isClassOrTypeElement(scope)) {
return rules.property;
}
else if (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',
category: 'Stylistic Issues',
recommended: 'error',
},
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}}'.",
},
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 === '?') {
// 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