tsfmt 0.0.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/build-plugins/transformGenericsPlugin.js +86 -0
- package/dist/cli.js +174 -0
- package/dist/core/ast/ASTAnalyzer.js +136 -0
- package/dist/core/ast/ASTTransformer.js +85 -0
- package/dist/core/ast/DependencyResolver.js +144 -0
- package/dist/core/config/ConfigDefaults.js +178 -0
- package/dist/core/config/ConfigLoader.js +251 -0
- package/dist/core/config/ConfigMerger.js +45 -0
- package/dist/core/config/ConfigTypes.js +70 -0
- package/dist/core/config/ConfigValidator.js +74 -0
- package/dist/core/di/Container.js +175 -0
- package/dist/core/di/ServiceRegistration.js +38 -0
- package/dist/core/formatters/BaseFormattingRule.js +37 -0
- package/dist/core/formatters/rules/ast/ClassMemberSortingRule.js +171 -0
- package/dist/core/formatters/rules/ast/FileDeclarationSortingRule.js +162 -0
- package/dist/core/formatters/rules/imports/ImportOrganizationRule.js +196 -0
- package/dist/core/formatters/rules/index-generation/IndexGenerationRule.js +208 -0
- package/dist/core/formatters/rules/spacing/BlankLineBeforeReturnsRule.js +32 -0
- package/dist/core/formatters/rules/spacing/BlankLineBetweenDeclarationsRule.js +93 -0
- package/dist/core/formatters/rules/spacing/BlankLineBetweenStatementTypesRule.js +57 -0
- package/dist/core/formatters/rules/spacing/BlockSpacingRule.js +17 -0
- package/dist/core/formatters/rules/spacing/BracketSpacingRule.js +108 -0
- package/dist/core/formatters/rules/style/DocBlockCommentRule.js +19 -0
- package/dist/core/formatters/rules/style/IndentationRule.js +45 -0
- package/dist/core/formatters/rules/style/QuoteStyleRule.js +63 -0
- package/dist/core/formatters/rules/style/SemicolonRule.js +68 -0
- package/dist/core/formatters/rules/style/StructuralIndentationRule.js +250 -0
- package/dist/core/pipeline/FormatterPipeline.js +252 -0
- package/dist/formatters/package.js +29 -0
- package/dist/index.js +66 -0
- package/dist/shared/types.js +28 -0
- package/dist/sortPackage.js +34 -0
- package/dist/sortTSConfig.js +34 -0
- package/package.json +11 -8
- package/bin/ci_build.sh +0 -50
- package/bin/cli.js +0 -20
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ts = require("typescript");
|
|
4
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
5
|
+
function _interopNamespaceDefault(e) {
|
|
6
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
7
|
+
if (e) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default") {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: () => e[k]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
n.default = e;
|
|
19
|
+
return Object.freeze(n);
|
|
20
|
+
}
|
|
21
|
+
const ts__namespace = /* @__PURE__ */ _interopNamespaceDefault(ts);
|
|
22
|
+
class BracketSpacingRule extends BaseFormattingRule.BaseFormattingRule {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.name = "BracketSpacingRule";
|
|
26
|
+
}
|
|
27
|
+
apply(source, filePath) {
|
|
28
|
+
const config = this.getCodeStyleConfig();
|
|
29
|
+
if (!config || config.bracketSpacing === void 0) {
|
|
30
|
+
return source;
|
|
31
|
+
}
|
|
32
|
+
const sourceFile = ts__namespace.createSourceFile("temp.ts", source, ts__namespace.ScriptTarget.Latest, true, ts__namespace.ScriptKind.TS);
|
|
33
|
+
const changes = [];
|
|
34
|
+
const fullText = sourceFile.getFullText();
|
|
35
|
+
const visit = (node) => {
|
|
36
|
+
if (ts__namespace.isObjectLiteralExpression(node)) {
|
|
37
|
+
const openBraceEnd = node.getStart(sourceFile) + 1;
|
|
38
|
+
const closeBraceStart = node.getEnd() - 1;
|
|
39
|
+
if (node.properties.length > 0) {
|
|
40
|
+
if (config.bracketSpacing) {
|
|
41
|
+
const afterOpenBrace = fullText[openBraceEnd];
|
|
42
|
+
if (afterOpenBrace !== " " && afterOpenBrace !== "\n") {
|
|
43
|
+
changes.push({ pos: openBraceEnd, type: "add", text: " " });
|
|
44
|
+
}
|
|
45
|
+
const beforeCloseBrace = fullText[closeBraceStart - 1];
|
|
46
|
+
if (beforeCloseBrace !== " " && beforeCloseBrace !== "\n") {
|
|
47
|
+
changes.push({ pos: closeBraceStart, type: "add", text: " " });
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
let pos = openBraceEnd;
|
|
51
|
+
while (fullText[pos] === " " || fullText[pos] === " ") {
|
|
52
|
+
changes.push({ pos, type: "remove" });
|
|
53
|
+
pos++;
|
|
54
|
+
}
|
|
55
|
+
pos = closeBraceStart - 1;
|
|
56
|
+
while (pos >= 0 && (fullText[pos] === " " || fullText[pos] === " ")) {
|
|
57
|
+
changes.push({ pos, type: "remove" });
|
|
58
|
+
pos--;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (ts__namespace.isNamedImports(node)) {
|
|
64
|
+
const parent = node.parent;
|
|
65
|
+
if (parent && ts__namespace.isImportClause(parent)) {
|
|
66
|
+
const openBraceEnd = node.getStart(sourceFile) + 1;
|
|
67
|
+
const closeBraceStart = node.getEnd() - 1;
|
|
68
|
+
if (node.elements.length > 0) {
|
|
69
|
+
if (config.bracketSpacing) {
|
|
70
|
+
const afterOpenBrace = fullText[openBraceEnd];
|
|
71
|
+
if (afterOpenBrace !== " ") {
|
|
72
|
+
changes.push({ pos: openBraceEnd, type: "add", text: " " });
|
|
73
|
+
}
|
|
74
|
+
const beforeCloseBrace = fullText[closeBraceStart - 1];
|
|
75
|
+
if (beforeCloseBrace !== " ") {
|
|
76
|
+
changes.push({ pos: closeBraceStart, type: "add", text: " " });
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
let pos = openBraceEnd;
|
|
80
|
+
while (fullText[pos] === " " || fullText[pos] === " ") {
|
|
81
|
+
changes.push({ pos, type: "remove" });
|
|
82
|
+
pos++;
|
|
83
|
+
}
|
|
84
|
+
pos = closeBraceStart - 1;
|
|
85
|
+
while (pos >= 0 && (fullText[pos] === " " || fullText[pos] === " ")) {
|
|
86
|
+
changes.push({ pos, type: "remove" });
|
|
87
|
+
pos--;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
ts__namespace.forEachChild(node, visit);
|
|
94
|
+
};
|
|
95
|
+
visit(sourceFile);
|
|
96
|
+
changes.sort((a, b) => b.pos - a.pos);
|
|
97
|
+
let result = source;
|
|
98
|
+
for (const change of changes) {
|
|
99
|
+
if (change.type === "add") {
|
|
100
|
+
result = result.substring(0, change.pos) + (change.text || " ") + result.substring(change.pos);
|
|
101
|
+
} else {
|
|
102
|
+
result = result.substring(0, change.pos) + result.substring(change.pos + 1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
exports.BracketSpacingRule = BracketSpacingRule;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
4
|
+
class DocBlockCommentRule extends BaseFormattingRule.BaseFormattingRule {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.name = "DocBlockCommentRule";
|
|
8
|
+
}
|
|
9
|
+
apply(source, filePath) {
|
|
10
|
+
const docBlockPattern = /\/\*\*\s*\n\s*\*\s*([^\n]*?)\s*\n\s*\*\//g;
|
|
11
|
+
return source.replace(docBlockPattern, (match, content) => {
|
|
12
|
+
if (content && content.trim()) {
|
|
13
|
+
return `/** ${content.trim()} */`;
|
|
14
|
+
}
|
|
15
|
+
return match;
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.DocBlockCommentRule = DocBlockCommentRule;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
4
|
+
class IndentationRule extends BaseFormattingRule.BaseFormattingRule {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.name = "IndentationRule";
|
|
8
|
+
}
|
|
9
|
+
apply(source, filePath) {
|
|
10
|
+
const config = this.getCodeStyleConfig();
|
|
11
|
+
if (!config?.indentStyle || !config.indentWidth) {
|
|
12
|
+
return source;
|
|
13
|
+
}
|
|
14
|
+
const indentWidth = config.indentWidth;
|
|
15
|
+
const lines = source.split("\n");
|
|
16
|
+
const result = [];
|
|
17
|
+
for (const line of lines) {
|
|
18
|
+
if (line.trim() === "") {
|
|
19
|
+
result.push(line);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const leadingWhitespace = line.match(/^\s*/)?.[0] || "";
|
|
23
|
+
const content = line.substring(leadingWhitespace.length);
|
|
24
|
+
let indentLevel = 0;
|
|
25
|
+
if (config.indentStyle === "space") {
|
|
26
|
+
const tabCount = (leadingWhitespace.match(/\t/g) || []).length;
|
|
27
|
+
const spaceCount = (leadingWhitespace.match(/ /g) || []).length;
|
|
28
|
+
indentLevel = tabCount + Math.floor(spaceCount / indentWidth);
|
|
29
|
+
} else {
|
|
30
|
+
const spaceCount = (leadingWhitespace.match(/ /g) || []).length;
|
|
31
|
+
const tabCount = (leadingWhitespace.match(/\t/g) || []).length;
|
|
32
|
+
indentLevel = tabCount + Math.floor(spaceCount / indentWidth);
|
|
33
|
+
}
|
|
34
|
+
let newIndent;
|
|
35
|
+
if (config.indentStyle === "space") {
|
|
36
|
+
newIndent = " ".repeat(indentLevel * indentWidth);
|
|
37
|
+
} else {
|
|
38
|
+
newIndent = " ".repeat(indentLevel);
|
|
39
|
+
}
|
|
40
|
+
result.push(newIndent + content);
|
|
41
|
+
}
|
|
42
|
+
return result.join("\n");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.IndentationRule = IndentationRule;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ts = require("typescript");
|
|
4
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
5
|
+
function _interopNamespaceDefault(e) {
|
|
6
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
7
|
+
if (e) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default") {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: () => e[k]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
n.default = e;
|
|
19
|
+
return Object.freeze(n);
|
|
20
|
+
}
|
|
21
|
+
const ts__namespace = /* @__PURE__ */ _interopNamespaceDefault(ts);
|
|
22
|
+
class QuoteStyleRule extends BaseFormattingRule.BaseFormattingRule {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.name = "QuoteStyleRule";
|
|
26
|
+
}
|
|
27
|
+
apply(source, filePath) {
|
|
28
|
+
const config = this.getCodeStyleConfig();
|
|
29
|
+
if (!config?.quoteStyle) {
|
|
30
|
+
return source;
|
|
31
|
+
}
|
|
32
|
+
const sourceFile = ts__namespace.createSourceFile("temp.ts", source, ts__namespace.ScriptTarget.Latest, true, ts__namespace.ScriptKind.TS);
|
|
33
|
+
const changes = [];
|
|
34
|
+
const visit = (node) => {
|
|
35
|
+
if (ts__namespace.isStringLiteral(node)) {
|
|
36
|
+
const nodeText = node.getText(sourceFile);
|
|
37
|
+
const currentQuote = nodeText[0];
|
|
38
|
+
const desiredQuote = config.quoteStyle === "single" ? "'" : '"';
|
|
39
|
+
if (currentQuote !== desiredQuote) {
|
|
40
|
+
const content = node.text;
|
|
41
|
+
const needsEscape = content.includes(desiredQuote);
|
|
42
|
+
if (!needsEscape) {
|
|
43
|
+
const newText = desiredQuote + content + desiredQuote;
|
|
44
|
+
changes.push({
|
|
45
|
+
start: node.getStart(sourceFile),
|
|
46
|
+
end: node.getEnd(),
|
|
47
|
+
text: newText
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
ts__namespace.forEachChild(node, visit);
|
|
53
|
+
};
|
|
54
|
+
visit(sourceFile);
|
|
55
|
+
changes.sort((a, b) => b.start - a.start);
|
|
56
|
+
let result = source;
|
|
57
|
+
for (const change of changes) {
|
|
58
|
+
result = result.substring(0, change.start) + change.text + result.substring(change.end);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.QuoteStyleRule = QuoteStyleRule;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ts = require("typescript");
|
|
4
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
5
|
+
function _interopNamespaceDefault(e) {
|
|
6
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
7
|
+
if (e) {
|
|
8
|
+
for (const k in e) {
|
|
9
|
+
if (k !== "default") {
|
|
10
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
11
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
get: () => e[k]
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
n.default = e;
|
|
19
|
+
return Object.freeze(n);
|
|
20
|
+
}
|
|
21
|
+
const ts__namespace = /* @__PURE__ */ _interopNamespaceDefault(ts);
|
|
22
|
+
class SemicolonRule extends BaseFormattingRule.BaseFormattingRule {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.name = "SemicolonRule";
|
|
26
|
+
}
|
|
27
|
+
apply(source, filePath) {
|
|
28
|
+
const config = this.getCodeStyleConfig();
|
|
29
|
+
if (!config?.semicolons) {
|
|
30
|
+
return source;
|
|
31
|
+
}
|
|
32
|
+
const sourceFile = ts__namespace.createSourceFile("temp.ts", source, ts__namespace.ScriptTarget.Latest, true, ts__namespace.ScriptKind.TS);
|
|
33
|
+
const changes = [];
|
|
34
|
+
const visit = (node) => {
|
|
35
|
+
if (ts__namespace.isVariableStatement(node) || ts__namespace.isExpressionStatement(node) || ts__namespace.isReturnStatement(node) || ts__namespace.isThrowStatement(node) || ts__namespace.isBreakStatement(node) || ts__namespace.isContinueStatement(node) || ts__namespace.isImportDeclaration(node) || ts__namespace.isExportDeclaration(node) || ts__namespace.isTypeAliasDeclaration(node)) {
|
|
36
|
+
const nodeEnd = node.getEnd();
|
|
37
|
+
const fullText = sourceFile.getFullText();
|
|
38
|
+
const hasSemicolon = fullText[nodeEnd - 1] === ";";
|
|
39
|
+
if (config.semicolons === "always" && !hasSemicolon) {
|
|
40
|
+
changes.push({ pos: nodeEnd, type: "add" });
|
|
41
|
+
} else if (config.semicolons === "never" && hasSemicolon) {
|
|
42
|
+
changes.push({ pos: nodeEnd - 1, type: "remove" });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (ts__namespace.isInterfaceDeclaration(node) || ts__namespace.isClassDeclaration(node) || ts__namespace.isEnumDeclaration(node)) {
|
|
46
|
+
const nodeEnd = node.getEnd();
|
|
47
|
+
const fullText = sourceFile.getFullText();
|
|
48
|
+
const hasSemicolon = fullText[nodeEnd] === ";";
|
|
49
|
+
if (hasSemicolon) {
|
|
50
|
+
changes.push({ pos: nodeEnd, type: "remove" });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
ts__namespace.forEachChild(node, visit);
|
|
54
|
+
};
|
|
55
|
+
visit(sourceFile);
|
|
56
|
+
changes.sort((a, b) => b.pos - a.pos);
|
|
57
|
+
let result = source;
|
|
58
|
+
for (const change of changes) {
|
|
59
|
+
if (change.type === "add") {
|
|
60
|
+
result = result.substring(0, change.pos) + ";" + result.substring(change.pos);
|
|
61
|
+
} else {
|
|
62
|
+
result = result.substring(0, change.pos) + result.substring(change.pos + 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.SemicolonRule = SemicolonRule;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const BaseFormattingRule = require("../../BaseFormattingRule.js");
|
|
4
|
+
class StructuralIndentationRule extends BaseFormattingRule.BaseFormattingRule {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.name = "StructuralIndentationRule";
|
|
8
|
+
}
|
|
9
|
+
skipString(source, start, quote) {
|
|
10
|
+
let i = start + 1;
|
|
11
|
+
let newlines = 0;
|
|
12
|
+
const isTemplate = quote === "`";
|
|
13
|
+
while (i < source.length) {
|
|
14
|
+
const char = source[i];
|
|
15
|
+
if (char === "\n") {
|
|
16
|
+
newlines++;
|
|
17
|
+
i++;
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (char === "\\") {
|
|
21
|
+
i += 2;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (isTemplate && char === "$" && source[i + 1] === "{") {
|
|
25
|
+
i += 2;
|
|
26
|
+
let braceCount = 1;
|
|
27
|
+
while (i < source.length && braceCount > 0) {
|
|
28
|
+
if (source[i] === "\n") {
|
|
29
|
+
newlines++;
|
|
30
|
+
} else if (source[i] === "{") {
|
|
31
|
+
braceCount++;
|
|
32
|
+
} else if (source[i] === "}") {
|
|
33
|
+
braceCount--;
|
|
34
|
+
} else if (source[i] === '"' || source[i] === "'" || source[i] === "`") {
|
|
35
|
+
const result = this.skipString(source, i, source[i]);
|
|
36
|
+
i = result.pos;
|
|
37
|
+
newlines += result.newlines;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (char === quote) {
|
|
45
|
+
return { pos: i + 1, newlines };
|
|
46
|
+
}
|
|
47
|
+
i++;
|
|
48
|
+
}
|
|
49
|
+
return { pos: i, newlines };
|
|
50
|
+
}
|
|
51
|
+
isRegexStart(source, index) {
|
|
52
|
+
let i = index - 1;
|
|
53
|
+
while (i >= 0 && (source[i] === " " || source[i] === " ")) {
|
|
54
|
+
i--;
|
|
55
|
+
}
|
|
56
|
+
if (i < 0) return true;
|
|
57
|
+
const char = source[i];
|
|
58
|
+
const regexPreceders = ["(", ",", "=", ":", "[", "!", "&", "|", "?", "{", "}", ";", "\n", "return", "case"];
|
|
59
|
+
if (regexPreceders.includes(char)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const keywords = ["return", "case", "typeof", "void", "delete", "throw", "in", "instanceof"];
|
|
63
|
+
for (const kw of keywords) {
|
|
64
|
+
if (index >= kw.length && source.substring(index - kw.length, index).endsWith(kw)) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
skipRegex(source, start) {
|
|
71
|
+
let i = start + 1;
|
|
72
|
+
let inCharClass = false;
|
|
73
|
+
while (i < source.length) {
|
|
74
|
+
const char = source[i];
|
|
75
|
+
if (char === "\\") {
|
|
76
|
+
i += 2;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (char === "[") {
|
|
80
|
+
inCharClass = true;
|
|
81
|
+
} else if (char === "]") {
|
|
82
|
+
inCharClass = false;
|
|
83
|
+
} else if (char === "/" && !inCharClass) {
|
|
84
|
+
i++;
|
|
85
|
+
while (i < source.length && /[gimsuy]/.test(source[i])) {
|
|
86
|
+
i++;
|
|
87
|
+
}
|
|
88
|
+
return i;
|
|
89
|
+
} else if (char === "\n") {
|
|
90
|
+
return i;
|
|
91
|
+
}
|
|
92
|
+
i++;
|
|
93
|
+
}
|
|
94
|
+
return i;
|
|
95
|
+
}
|
|
96
|
+
getLineIndentLevel(line, indentWidth) {
|
|
97
|
+
const leadingWhitespace = line.match(/^[\t ]*/)?.[0] || "";
|
|
98
|
+
const tabCount = (leadingWhitespace.match(/\t/g) || []).length;
|
|
99
|
+
const spaceCount = (leadingWhitespace.match(/ /g) || []).length;
|
|
100
|
+
return tabCount + Math.floor(spaceCount / indentWidth);
|
|
101
|
+
}
|
|
102
|
+
startsWithClosingBracket(trimmedLine) {
|
|
103
|
+
return /^[}\])]/.test(trimmedLine);
|
|
104
|
+
}
|
|
105
|
+
findBracketFixes(source, lines, indentWidth) {
|
|
106
|
+
const fixes = [];
|
|
107
|
+
const stack = [];
|
|
108
|
+
const lineIndentCorrections = /* @__PURE__ */ new Map();
|
|
109
|
+
let i = 0;
|
|
110
|
+
let line = 0;
|
|
111
|
+
let column = 0;
|
|
112
|
+
let lineStart = 0;
|
|
113
|
+
const openBrackets = {
|
|
114
|
+
"{": "}",
|
|
115
|
+
"[": "]",
|
|
116
|
+
"(": ")"
|
|
117
|
+
};
|
|
118
|
+
const closeBrackets = {
|
|
119
|
+
"}": "{",
|
|
120
|
+
"]": "[",
|
|
121
|
+
")": "("
|
|
122
|
+
};
|
|
123
|
+
while (i < source.length) {
|
|
124
|
+
const char = source[i];
|
|
125
|
+
if (char === "\n") {
|
|
126
|
+
line++;
|
|
127
|
+
column = 0;
|
|
128
|
+
lineStart = i + 1;
|
|
129
|
+
i++;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
133
|
+
const result = this.skipString(source, i, char);
|
|
134
|
+
i = result.pos;
|
|
135
|
+
line += result.newlines;
|
|
136
|
+
if (result.newlines > 0) {
|
|
137
|
+
let lastNewline = i - 1;
|
|
138
|
+
while (lastNewline >= 0 && source[lastNewline] !== "\n") {
|
|
139
|
+
lastNewline--;
|
|
140
|
+
}
|
|
141
|
+
lineStart = lastNewline + 1;
|
|
142
|
+
}
|
|
143
|
+
column = i - lineStart;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (char === "/" && source[i + 1] === "/") {
|
|
147
|
+
while (i < source.length && source[i] !== "\n") {
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (char === "/" && source[i + 1] === "*") {
|
|
153
|
+
i += 2;
|
|
154
|
+
while (i < source.length - 1 && !(source[i] === "*" && source[i + 1] === "/")) {
|
|
155
|
+
if (source[i] === "\n") {
|
|
156
|
+
line++;
|
|
157
|
+
lineStart = i + 1;
|
|
158
|
+
}
|
|
159
|
+
i++;
|
|
160
|
+
}
|
|
161
|
+
i += 2;
|
|
162
|
+
column = i - lineStart;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (char === "/" && this.isRegexStart(source, i)) {
|
|
166
|
+
i = this.skipRegex(source, i);
|
|
167
|
+
column = i - lineStart;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (openBrackets[char]) {
|
|
171
|
+
const lineIndent = lineIndentCorrections.has(line) ? lineIndentCorrections.get(line) : this.getLineIndentLevel(lines[line], indentWidth);
|
|
172
|
+
stack.push({
|
|
173
|
+
char,
|
|
174
|
+
position: i,
|
|
175
|
+
line,
|
|
176
|
+
lineIndent
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (closeBrackets[char]) {
|
|
180
|
+
const expectedOpen = closeBrackets[char];
|
|
181
|
+
let matchIndex = -1;
|
|
182
|
+
for (let j = stack.length - 1; j >= 0; j--) {
|
|
183
|
+
if (stack[j].char === expectedOpen) {
|
|
184
|
+
matchIndex = j;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (matchIndex !== -1) {
|
|
189
|
+
const openBracket = stack[matchIndex];
|
|
190
|
+
stack.splice(matchIndex, 1);
|
|
191
|
+
if (line !== openBracket.line) {
|
|
192
|
+
const currentIndent = this.getLineIndentLevel(lines[line], indentWidth);
|
|
193
|
+
const trimmedLine = lines[line].trimStart();
|
|
194
|
+
if (this.startsWithClosingBracket(trimmedLine)) {
|
|
195
|
+
if (currentIndent !== openBracket.lineIndent) {
|
|
196
|
+
fixes.push({
|
|
197
|
+
position: i,
|
|
198
|
+
line,
|
|
199
|
+
column,
|
|
200
|
+
targetIndent: openBracket.lineIndent
|
|
201
|
+
});
|
|
202
|
+
if (!lineIndentCorrections.has(line)) {
|
|
203
|
+
lineIndentCorrections.set(line, openBracket.lineIndent);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
i++;
|
|
211
|
+
column++;
|
|
212
|
+
}
|
|
213
|
+
return fixes;
|
|
214
|
+
}
|
|
215
|
+
apply(source, filePath) {
|
|
216
|
+
const config = this.getCodeStyleConfig();
|
|
217
|
+
if (!config?.indentStyle || !config.indentWidth) {
|
|
218
|
+
return source;
|
|
219
|
+
}
|
|
220
|
+
const indentWidth = config.indentWidth;
|
|
221
|
+
const indentUnit = config.indentStyle === "tab" ? " " : " ".repeat(indentWidth);
|
|
222
|
+
const lines = source.split("\n");
|
|
223
|
+
const fixes = this.findBracketFixes(source, lines, indentWidth);
|
|
224
|
+
if (fixes.length === 0) {
|
|
225
|
+
return source;
|
|
226
|
+
}
|
|
227
|
+
const fixesByLine = /* @__PURE__ */ new Map();
|
|
228
|
+
for (const fix of fixes) {
|
|
229
|
+
if (!fixesByLine.has(fix.line)) {
|
|
230
|
+
fixesByLine.set(fix.line, []);
|
|
231
|
+
}
|
|
232
|
+
fixesByLine.get(fix.line).push(fix);
|
|
233
|
+
}
|
|
234
|
+
const result = [];
|
|
235
|
+
for (let i = 0; i < lines.length; i++) {
|
|
236
|
+
const lineFixes = fixesByLine.get(i);
|
|
237
|
+
if (lineFixes && lineFixes.length > 0) {
|
|
238
|
+
lineFixes.sort((a, b) => a.column - b.column);
|
|
239
|
+
const primaryFix = lineFixes[0];
|
|
240
|
+
const trimmedLine = lines[i].trimStart();
|
|
241
|
+
const newIndent = indentUnit.repeat(primaryFix.targetIndent);
|
|
242
|
+
result.push(newIndent + trimmedLine);
|
|
243
|
+
} else {
|
|
244
|
+
result.push(lines[i]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return result.join("\n");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
exports.StructuralIndentationRule = StructuralIndentationRule;
|