eslint-plugin-sql
Version:
SQL linting rules for ESLint.
233 lines (232 loc) • 9.5 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = void 0;
const createRule_1 = require("../factories/createRule");
const dropBaseIndent_1 = require("../utilities/dropBaseIndent");
const isSqlQuery_1 = require("../utilities/isSqlQuery");
const utils_1 = require("@typescript-eslint/utils");
const sql_formatter_1 = require("sql-formatter");
const padIndent = (subject, spaces) => {
return subject
.split('\n')
.map((line) => {
return line.length > 0 ? ' '.repeat(spaces) + line : line;
})
.join('\n');
};
const findFirstMeaningfulIndent = (subject) => {
for (const line of subject.split('\n')) {
if (line.trim().length > 0) {
return line.search(/\S/u);
}
}
return 0;
};
exports.rule = (0, createRule_1.createRule)({
create: (context) => {
var _a, _b, _c, _d, _e, _f;
// @ts-expect-error I am ont clear how to type this
const placeholderRule = (_b = (_a = context.settings) === null || _a === void 0 ? void 0 : _a.sql) === null || _b === void 0 ? void 0 : _b.placeholderRule;
const pluginOptions = ((_c = context.options) === null || _c === void 0 ? void 0 : _c[0]) || {
ignoreExpressions: false,
ignoreInline: true,
ignoreStartWithNewLine: true,
ignoreTagless: true,
retainBaseIndent: true,
sqlTag: 'sql',
};
const { ignoreExpressions, ignoreInline, ignoreStartWithNewLine, ignoreTagless, retainBaseIndent, sqlTag, } = pluginOptions;
const tabWidth = (_f = (_e = (_d = context.options) === null || _d === void 0 ? void 0 : _d[1]) === null || _e === void 0 ? void 0 : _e.tabWidth) !== null && _f !== void 0 ? _f : 2;
return {
TemplateLiteral(node) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const tagName =
// @ts-expect-error TODO
(_e = (_b = (_a = node.parent.tag) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b :
// @ts-expect-error TODO
(_d = (_c = node.parent.tag) === null || _c === void 0 ? void 0 : _c.object) === null || _d === void 0 ? void 0 : _d.name) !== null && _e !== void 0 ? _e :
// @ts-expect-error TODO
(_h = (_g = (_f = node.parent.tag) === null || _f === void 0 ? void 0 : _f.callee) === null || _g === void 0 ? void 0 : _g.object) === null || _h === void 0 ? void 0 : _h.name;
const sqlTagIsPresent = tagName === sqlTag;
if (ignoreTagless && !sqlTagIsPresent) {
return;
}
if (ignoreExpressions && node.quasis.length !== 1) {
return;
}
const templateElement = node.quasis.find((quasi) => {
return quasi.type === utils_1.AST_NODE_TYPES.TemplateElement;
});
if (!templateElement) {
return;
}
let indentAnchorOffset = findFirstMeaningfulIndent(templateElement.value.raw);
if (templateElement.value.raw.search(/\S/u) === -1) {
const lines = templateElement.value.raw.split('\n');
const lastLine = lines[lines.length - 1];
if (lastLine) {
indentAnchorOffset = lastLine.length;
}
else {
indentAnchorOffset = 0;
}
}
else if (templateElement.value.raw.search(/\S/u) === 0) {
indentAnchorOffset = tabWidth;
}
const magic = '"gajus-eslint-plugin-sql"';
const literal = node.quasis
.map((quasi) => {
return quasi.value.raw;
})
.join(magic);
if (!sqlTagIsPresent && !(0, isSqlQuery_1.isSqlQuery)(literal, placeholderRule)) {
return;
}
if (ignoreInline && !literal.includes('\n')) {
return;
}
let formatted = (0, sql_formatter_1.format)(literal, {
...context.options[1],
tabWidth,
});
if (ignoreStartWithNewLine &&
literal.startsWith('\n') &&
!formatted.startsWith('\n')) {
formatted = '\n' + formatted;
}
if (formatted.endsWith('\n\n')) {
formatted = formatted.replace(/\n\n$/u, '\n');
}
if (retainBaseIndent) {
formatted = padIndent(formatted, indentAnchorOffset);
}
else {
formatted = (0, dropBaseIndent_1.dropBaseIndent)(literal);
}
formatted +=
'\n' + ' '.repeat(Math.max(indentAnchorOffset - tabWidth, 0));
if (formatted !== literal) {
context.report({
fix: (fixer) => {
let final = formatted;
const expressionCount = node.expressions.length;
let index = 0;
while (index <= expressionCount - 1) {
final = final.replace(magic, '${' +
context.sourceCode.getText(node.expressions[index]) +
'}');
index++;
}
return fixer.replaceTextRange([
node.quasis[0].range[0],
node.quasis[node.quasis.length - 1].range[1],
], '`' + (final.startsWith('\n') ? final : '\n' + final) + '`');
},
messageId: 'format',
node,
});
}
},
};
},
defaultOptions: [
{
ignoreExpressions: false,
ignoreInline: true,
ignoreStartWithNewLine: true,
ignoreTagless: true,
retainBaseIndent: true,
sqlTag: 'sql',
},
{
tabWidth: 2,
},
],
meta: {
docs: {
description: 'Matches queries in template literals. Warns when query formatting does not match the configured format (see Options).',
url: 'https://github.com/gajus/eslint-plugin-sql#eslint-plugin-sql-rules-format',
},
fixable: 'code',
messages: {
format: 'Format the query',
},
schema: [
{
additionalProperties: false,
properties: {
ignoreExpressions: {
default: false,
type: 'boolean',
},
ignoreInline: {
default: true,
type: 'boolean',
},
ignoreStartWithNewLine: {
default: true,
type: 'boolean',
},
ignoreTagless: {
default: true,
type: 'boolean',
},
retainBaseIndent: {
default: true,
type: 'boolean',
},
sqlTag: {
default: 'sql',
type: 'string',
},
},
type: 'object',
},
{
additionalProperties: false,
properties: {
dataTypeCase: {
default: 'preserve',
enum: ['lower', 'upper', 'preserve'],
type: 'string',
},
denseOperators: {
default: false,
type: 'boolean',
},
functionCase: {
default: 'preserve',
enum: ['lower', 'upper', 'preserve'],
type: 'string',
},
identifierCase: {
default: 'preserve',
enum: ['lower', 'upper', 'preserve'],
type: 'string',
},
keywordCase: {
default: 'preserve',
enum: ['lower', 'upper', 'preserve'],
type: 'string',
},
language: {
default: 'sql',
type: 'string',
},
tabWidth: {
default: 2,
type: 'number',
},
useTabs: {
default: false,
type: 'boolean',
},
},
type: 'object',
},
],
type: 'suggestion',
},
name: 'format',
});