Skip to content

Commit c2b94cd

Browse files
committed
feat: add alignIndent option
1 parent 738873a commit c2b94cd

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

.README/rules/format.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The first option is an object with the following configuration.
1212

1313
|configuration|format|default|description|
1414
|---|---|---|---|
15+
|`padIndent`|boolean|`false`|Aligns indentation with either the start of the `TemplateLiteral` or `TaggedTemplateExpression` node.|
1516
|`ignoreBaseIndent`|boolean|`false`|Does not leave base indent before linting.|
1617
|`ignoreExpressions`|boolean|`false`|Does not format template literals that contain expressions.|
1718
|`ignoreInline`|boolean|`true`|Does not format queries that are written on a single line.|

src/rules/format.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,33 @@ export default createRuleTester(
3333
\`
3434
`,
3535
},
36+
{
37+
code: multiline`
38+
sql\`
39+
SELECT
40+
1
41+
\`
42+
`,
43+
errors: [
44+
{
45+
messageId: 'format',
46+
},
47+
],
48+
options: [
49+
{
50+
alignIndent: true,
51+
},
52+
{
53+
spaces: 4,
54+
},
55+
],
56+
output: multiline`
57+
sql\`
58+
SELECT
59+
1
60+
\`
61+
`,
62+
},
3663
{
3764
code: multiline`
3865
sql.type({ id: z.number() })\`

src/rules/format.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { createRule } from '../factories/createRule';
22
import { dropBaseIndent } from '../utilities/dropBaseIndent';
33
import { isSqlQuery } from '../utilities/isSqlQuery';
4+
import { AST_NODE_TYPES } from '@typescript-eslint/utils';
45
import { generate } from 'astring';
56
import { format } from 'pg-formatter';
67

78
type MessageIds = 'format';
89

910
type Options = [
1011
{
12+
alignIndent?: boolean;
1113
ignoreBaseIndent?: boolean;
1214
ignoreExpressions?: boolean;
1315
ignoreInline?: boolean;
@@ -27,13 +29,23 @@ type Options = [
2729
},
2830
];
2931

32+
const padIndent = (subject: string, spaces: number) => {
33+
return subject
34+
.split('\n')
35+
.map((line) => {
36+
return line.length > 0 ? ' '.repeat(spaces) + line : line;
37+
})
38+
.join('\n');
39+
};
40+
3041
export const rule = createRule<Options, MessageIds>({
3142
create: (context) => {
3243
// @ts-expect-error I am ont clear how to type this
3344
const placeholderRule = context.settings?.sql?.placeholderRule;
3445

3546
const pluginOptions = context.options?.[0] || {};
3647

48+
const alignIndent = pluginOptions.alignIndent === true;
3749
const sqlTag = pluginOptions.sqlTag ?? 'sql';
3850
const ignoreExpressions = pluginOptions.ignoreExpressions === true;
3951
const ignoreInline = pluginOptions.ignoreInline !== false;
@@ -42,6 +54,8 @@ export const rule = createRule<Options, MessageIds>({
4254
pluginOptions.ignoreStartWithNewLine !== false;
4355
const ignoreBaseIndent = pluginOptions.ignoreBaseIndent === true;
4456

57+
const spaces = context.options?.[1]?.spaces ?? 4;
58+
4559
return {
4660
TemplateLiteral(node) {
4761
const tagName =
@@ -54,6 +68,14 @@ export const rule = createRule<Options, MessageIds>({
5468

5569
const sqlTagIsPresent = tagName === sqlTag;
5670

71+
let indentAnchorOffset = node.loc.start.column;
72+
73+
if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) {
74+
indentAnchorOffset = node.parent.loc.start.column;
75+
}
76+
77+
indentAnchorOffset += spaces;
78+
5779
if (ignoreTagless && !sqlTagIsPresent) {
5880
return;
5981
}
@@ -82,7 +104,10 @@ export const rule = createRule<Options, MessageIds>({
82104
literal = dropBaseIndent(literal);
83105
}
84106

85-
let formatted = format(literal, context.options[1]);
107+
let formatted = format(literal, {
108+
...context.options[1],
109+
spaces,
110+
});
86111

87112
if (
88113
ignoreStartWithNewLine &&
@@ -96,6 +121,10 @@ export const rule = createRule<Options, MessageIds>({
96121
formatted = formatted.replace(/\n\n$/u, '\n');
97122
}
98123

124+
if (alignIndent) {
125+
formatted = padIndent(formatted, indentAnchorOffset);
126+
}
127+
99128
if (formatted !== literal) {
100129
context.report({
101130
fix: (fixer) => {
@@ -130,6 +159,7 @@ export const rule = createRule<Options, MessageIds>({
130159
},
131160
defaultOptions: [
132161
{
162+
alignIndent: true,
133163
ignoreBaseIndent: false,
134164
ignoreExpressions: false,
135165
ignoreInline: true,
@@ -162,6 +192,10 @@ export const rule = createRule<Options, MessageIds>({
162192
{
163193
additionalProperties: false,
164194
properties: {
195+
alignIndent: {
196+
default: false,
197+
type: 'boolean',
198+
},
165199
ignoreBaseIndent: {
166200
default: false,
167201
type: 'boolean',

0 commit comments

Comments
 (0)