eslint-plugin-markdown-preferences 0.19.0 → 0.20.0
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 +2 -0
- package/lib/index.d.ts +23 -1
- package/lib/index.js +295 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -118,6 +118,8 @@ The rules with the following star ⭐ are included in the configs.
|
|
|
118
118
|
| [markdown-preferences/atx-heading-closing-sequence](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/atx-heading-closing-sequence.html) | enforce consistent use of closing sequence in ATX headings. | 🔧 | |
|
|
119
119
|
| [markdown-preferences/blockquote-marker-alignment](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/blockquote-marker-alignment.html) | enforce consistent alignment of blockquote markers | 🔧 | ⭐ |
|
|
120
120
|
| [markdown-preferences/bullet-list-marker-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/bullet-list-marker-style.html) | enforce consistent bullet list (unordered list) marker style | 🔧 | |
|
|
121
|
+
| [markdown-preferences/code-fence-length](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html) | enforce consistent code fence length in fenced code blocks. | 🔧 | |
|
|
122
|
+
| [markdown-preferences/code-fence-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-style.html) | enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks. | 🔧 | |
|
|
121
123
|
| [markdown-preferences/definitions-last](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/definitions-last.html) | require link definitions and footnote definitions to be placed at the end of the document | 🔧 | |
|
|
122
124
|
| [markdown-preferences/emphasis-delimiters-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/emphasis-delimiters-style.html) | enforce a consistent delimiter style for emphasis and strong emphasis | 🔧 | |
|
|
123
125
|
| [markdown-preferences/hard-linebreak-style](https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/hard-linebreak-style.html) | enforce consistent hard linebreak style. | 🔧 | ⭐ |
|
package/lib/index.d.ts
CHANGED
|
@@ -35,6 +35,16 @@ interface RuleOptions {
|
|
|
35
35
|
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/canonical-code-block-language.html
|
|
36
36
|
*/
|
|
37
37
|
'markdown-preferences/canonical-code-block-language'?: Linter.RuleEntry<MarkdownPreferencesCanonicalCodeBlockLanguage>;
|
|
38
|
+
/**
|
|
39
|
+
* enforce consistent code fence length in fenced code blocks.
|
|
40
|
+
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-length.html
|
|
41
|
+
*/
|
|
42
|
+
'markdown-preferences/code-fence-length'?: Linter.RuleEntry<MarkdownPreferencesCodeFenceLength>;
|
|
43
|
+
/**
|
|
44
|
+
* enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.
|
|
45
|
+
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/code-fence-style.html
|
|
46
|
+
*/
|
|
47
|
+
'markdown-preferences/code-fence-style'?: Linter.RuleEntry<MarkdownPreferencesCodeFenceStyle>;
|
|
38
48
|
/**
|
|
39
49
|
* require link definitions and footnote definitions to be placed at the end of the document
|
|
40
50
|
* @see https://ota-meshi.github.io/eslint-plugin-markdown-preferences/rules/definitions-last.html
|
|
@@ -198,6 +208,18 @@ type MarkdownPreferencesCanonicalCodeBlockLanguage = [] | [{
|
|
|
198
208
|
[k: string]: string;
|
|
199
209
|
};
|
|
200
210
|
}];
|
|
211
|
+
type MarkdownPreferencesCodeFenceLength = [] | [{
|
|
212
|
+
length?: number;
|
|
213
|
+
fallbackLength?: (number | ("minimum" | "as-is"));
|
|
214
|
+
overrides?: {
|
|
215
|
+
lang: string;
|
|
216
|
+
length?: number;
|
|
217
|
+
fallbackLength?: (number | ("minimum" | "as-is"));
|
|
218
|
+
}[];
|
|
219
|
+
}];
|
|
220
|
+
type MarkdownPreferencesCodeFenceStyle = [] | [{
|
|
221
|
+
style?: ("backtick" | "tilde");
|
|
222
|
+
}];
|
|
201
223
|
type MarkdownPreferencesEmojiNotation = [] | [{
|
|
202
224
|
prefer?: ("unicode" | "colon");
|
|
203
225
|
ignoreUnknown?: boolean;
|
|
@@ -346,7 +368,7 @@ declare namespace meta_d_exports {
|
|
|
346
368
|
export { name, version };
|
|
347
369
|
}
|
|
348
370
|
declare const name: "eslint-plugin-markdown-preferences";
|
|
349
|
-
declare const version: "0.
|
|
371
|
+
declare const version: "0.20.0";
|
|
350
372
|
//#endregion
|
|
351
373
|
//#region src/index.d.ts
|
|
352
374
|
declare const configs: {
|
package/lib/index.js
CHANGED
|
@@ -873,6 +873,108 @@ var bullet_list_marker_style_default = createRule("bullet-list-marker-style", {
|
|
|
873
873
|
}
|
|
874
874
|
});
|
|
875
875
|
|
|
876
|
+
//#endregion
|
|
877
|
+
//#region src/utils/fenced-code-block.ts
|
|
878
|
+
const RE_OPENING_FENCE = /^(`{3,}|~{3,})/u;
|
|
879
|
+
const RE_LANGUAGE = /^(\w*)/u;
|
|
880
|
+
/**
|
|
881
|
+
* Parse the fenced code block.
|
|
882
|
+
*/
|
|
883
|
+
function parseFencedCodeBlock(sourceCode, node) {
|
|
884
|
+
const loc = sourceCode.getLoc(node);
|
|
885
|
+
const range = sourceCode.getRange(node);
|
|
886
|
+
const text = sourceCode.text.slice(...range);
|
|
887
|
+
const match = RE_OPENING_FENCE.exec(text);
|
|
888
|
+
if (!match) return null;
|
|
889
|
+
const [, fenceText] = match;
|
|
890
|
+
const fenceChar = fenceText[0];
|
|
891
|
+
let closingFenceText = "";
|
|
892
|
+
const trimmed = text.trimEnd();
|
|
893
|
+
const trailingSpacesLength = text.length - trimmed.length;
|
|
894
|
+
for (let index = trimmed.length - 1; index >= 0; index--) {
|
|
895
|
+
const c = trimmed[index];
|
|
896
|
+
if (c === fenceChar || isSpaceOrTab(c)) {
|
|
897
|
+
closingFenceText = c + closingFenceText;
|
|
898
|
+
continue;
|
|
899
|
+
}
|
|
900
|
+
if (c === "\n") break;
|
|
901
|
+
return null;
|
|
902
|
+
}
|
|
903
|
+
closingFenceText = closingFenceText.trimStart();
|
|
904
|
+
if (!closingFenceText || !closingFenceText.startsWith(fenceText)) return null;
|
|
905
|
+
const lines = getParsedLines(sourceCode);
|
|
906
|
+
const afterOpeningFence = lines.get(loc.start.line).text.slice(fenceText.length);
|
|
907
|
+
const trimmedAfterOpeningFence = afterOpeningFence.trimStart();
|
|
908
|
+
const spaceAfterOpeningFenceLength = afterOpeningFence.length - trimmedAfterOpeningFence.length;
|
|
909
|
+
let languageText = "";
|
|
910
|
+
if (trimmedAfterOpeningFence) {
|
|
911
|
+
const langMatch = RE_LANGUAGE.exec(trimmedAfterOpeningFence);
|
|
912
|
+
languageText = langMatch[1];
|
|
913
|
+
}
|
|
914
|
+
const afterLanguage = trimmedAfterOpeningFence.slice(languageText.length);
|
|
915
|
+
const trimmedAfterLanguage = afterLanguage.trimStart();
|
|
916
|
+
const spaceAfterLanguageLength = afterLanguage.length - trimmedAfterLanguage.length;
|
|
917
|
+
const metaText = trimmedAfterLanguage.trimEnd();
|
|
918
|
+
const openingFence = {
|
|
919
|
+
text: fenceText,
|
|
920
|
+
range: [range[0], range[0] + fenceText.length],
|
|
921
|
+
loc: {
|
|
922
|
+
start: loc.start,
|
|
923
|
+
end: {
|
|
924
|
+
line: loc.start.line,
|
|
925
|
+
column: loc.start.column + fenceText.length
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
const language$1 = languageText ? {
|
|
930
|
+
text: languageText,
|
|
931
|
+
range: [openingFence.range[1] + spaceAfterOpeningFenceLength, openingFence.range[1] + spaceAfterOpeningFenceLength + languageText.length],
|
|
932
|
+
loc: {
|
|
933
|
+
start: {
|
|
934
|
+
line: openingFence.loc.end.line,
|
|
935
|
+
column: openingFence.loc.end.column + spaceAfterOpeningFenceLength
|
|
936
|
+
},
|
|
937
|
+
end: {
|
|
938
|
+
line: openingFence.loc.end.line,
|
|
939
|
+
column: openingFence.loc.end.column + spaceAfterOpeningFenceLength + languageText.length
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
} : null;
|
|
943
|
+
const meta = language$1 && metaText ? {
|
|
944
|
+
text: metaText,
|
|
945
|
+
range: [language$1.range[1] + spaceAfterLanguageLength, language$1.range[1] + spaceAfterLanguageLength + metaText.length],
|
|
946
|
+
loc: {
|
|
947
|
+
start: {
|
|
948
|
+
line: language$1.loc.end.line,
|
|
949
|
+
column: language$1.loc.end.column + spaceAfterLanguageLength
|
|
950
|
+
},
|
|
951
|
+
end: {
|
|
952
|
+
line: language$1.loc.end.line,
|
|
953
|
+
column: language$1.loc.end.column + spaceAfterLanguageLength + metaText.length
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
} : null;
|
|
957
|
+
return {
|
|
958
|
+
openingFence,
|
|
959
|
+
language: language$1,
|
|
960
|
+
meta,
|
|
961
|
+
closingFence: {
|
|
962
|
+
text: closingFenceText,
|
|
963
|
+
range: [range[1] - trailingSpacesLength - closingFenceText.length, range[1] - trailingSpacesLength],
|
|
964
|
+
loc: {
|
|
965
|
+
start: {
|
|
966
|
+
line: loc.end.line,
|
|
967
|
+
column: loc.end.column - trailingSpacesLength - closingFenceText.length
|
|
968
|
+
},
|
|
969
|
+
end: {
|
|
970
|
+
line: loc.end.line,
|
|
971
|
+
column: loc.end.column - trailingSpacesLength
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
876
978
|
//#endregion
|
|
877
979
|
//#region src/rules/canonical-code-block-language.ts
|
|
878
980
|
const DEFAULT_LANGUAGES = {
|
|
@@ -927,23 +1029,204 @@ var canonical_code_block_language_default = createRule("canonical-code-block-lan
|
|
|
927
1029
|
const canonical = languages[node.lang];
|
|
928
1030
|
const current = node.lang;
|
|
929
1031
|
if (current === canonical) return;
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
const fenceRegex = /^(`{3,}|~{3,})(\w*)(?:\s.*)?$/mu;
|
|
933
|
-
const fenceMatch = fenceRegex.exec(nodeText.split("\n")[0]);
|
|
934
|
-
if (!fenceMatch) return;
|
|
935
|
-
const [, fence, langInfo] = fenceMatch;
|
|
936
|
-
const range = [nodeRange[0] + fence.length, nodeRange[0] + fence.length + langInfo.length];
|
|
1032
|
+
const parsed = parseFencedCodeBlock(sourceCode, node);
|
|
1033
|
+
if (!parsed || !parsed.language) return;
|
|
937
1034
|
context.report({
|
|
938
1035
|
node,
|
|
939
|
-
loc:
|
|
1036
|
+
loc: parsed.language.loc,
|
|
940
1037
|
messageId: "useCanonical",
|
|
941
1038
|
data: {
|
|
942
1039
|
canonical,
|
|
943
1040
|
current
|
|
944
1041
|
},
|
|
945
1042
|
fix(fixer) {
|
|
946
|
-
return fixer.replaceTextRange(range, canonical);
|
|
1043
|
+
return fixer.replaceTextRange(parsed.language.range, canonical);
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
} };
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
//#endregion
|
|
1051
|
+
//#region src/rules/code-fence-length.ts
|
|
1052
|
+
var code_fence_length_default = createRule("code-fence-length", {
|
|
1053
|
+
meta: {
|
|
1054
|
+
type: "layout",
|
|
1055
|
+
docs: {
|
|
1056
|
+
description: "enforce consistent code fence length in fenced code blocks.",
|
|
1057
|
+
categories: [],
|
|
1058
|
+
listCategory: "Stylistic"
|
|
1059
|
+
},
|
|
1060
|
+
fixable: "code",
|
|
1061
|
+
hasSuggestions: false,
|
|
1062
|
+
schema: [{
|
|
1063
|
+
type: "object",
|
|
1064
|
+
properties: {
|
|
1065
|
+
length: {
|
|
1066
|
+
type: "integer",
|
|
1067
|
+
minimum: 3
|
|
1068
|
+
},
|
|
1069
|
+
fallbackLength: { anyOf: [{
|
|
1070
|
+
type: "integer",
|
|
1071
|
+
minimum: 3
|
|
1072
|
+
}, { enum: ["minimum", "as-is"] }] },
|
|
1073
|
+
overrides: {
|
|
1074
|
+
type: "array",
|
|
1075
|
+
items: {
|
|
1076
|
+
type: "object",
|
|
1077
|
+
properties: {
|
|
1078
|
+
lang: { type: "string" },
|
|
1079
|
+
length: {
|
|
1080
|
+
type: "integer",
|
|
1081
|
+
minimum: 3
|
|
1082
|
+
},
|
|
1083
|
+
fallbackLength: { anyOf: [{
|
|
1084
|
+
type: "integer",
|
|
1085
|
+
minimum: 3
|
|
1086
|
+
}, { enum: ["minimum", "as-is"] }] }
|
|
1087
|
+
},
|
|
1088
|
+
required: ["lang"],
|
|
1089
|
+
additionalProperties: false
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
},
|
|
1093
|
+
additionalProperties: false
|
|
1094
|
+
}],
|
|
1095
|
+
messages: {
|
|
1096
|
+
notMatch: "The opening and closing code fence lengths must match.",
|
|
1097
|
+
notPreferred: "Code fence length should be {{expected}} (was {{actual}})."
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
create(context) {
|
|
1101
|
+
const sourceCode = context.sourceCode;
|
|
1102
|
+
const options = context.options[0] ?? {};
|
|
1103
|
+
/**
|
|
1104
|
+
* Get the effective options for the given code block node.
|
|
1105
|
+
*/
|
|
1106
|
+
function getOptionForCode(node) {
|
|
1107
|
+
const override = options.overrides?.find((o) => o.lang === node.lang);
|
|
1108
|
+
return {
|
|
1109
|
+
length: override?.length ?? options.length ?? 3,
|
|
1110
|
+
fallbackLength: override?.fallbackLength ?? options.fallbackLength ?? "minimum"
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Report the given code block node for not preferred length.
|
|
1115
|
+
*/
|
|
1116
|
+
function reportNotPreferred(node, parsed, length) {
|
|
1117
|
+
const expectedFence = getExpectedFence(parsed, length);
|
|
1118
|
+
context.report({
|
|
1119
|
+
node,
|
|
1120
|
+
loc: parsed.openingFence.loc,
|
|
1121
|
+
data: {
|
|
1122
|
+
expected: expectedFence,
|
|
1123
|
+
actual: parsed.openingFence.text
|
|
1124
|
+
},
|
|
1125
|
+
messageId: "notPreferred",
|
|
1126
|
+
fix(fixer) {
|
|
1127
|
+
return [fixer.replaceTextRange(parsed.openingFence.range, expectedFence), fixer.replaceTextRange(parsed.closingFence.range, expectedFence)];
|
|
1128
|
+
}
|
|
1129
|
+
});
|
|
1130
|
+
}
|
|
1131
|
+
/**
|
|
1132
|
+
* Verify the length of the given code block node.
|
|
1133
|
+
*/
|
|
1134
|
+
function verifyFenceLength(node, parsed) {
|
|
1135
|
+
const { length, fallbackLength } = getOptionForCode(node);
|
|
1136
|
+
if (parsed.openingFence.text.length === length) return true;
|
|
1137
|
+
const expectedFence = getExpectedFence(parsed, length);
|
|
1138
|
+
if (!node.value.includes(expectedFence)) {
|
|
1139
|
+
reportNotPreferred(node, parsed, length);
|
|
1140
|
+
return false;
|
|
1141
|
+
}
|
|
1142
|
+
if (fallbackLength === "as-is") return true;
|
|
1143
|
+
if (fallbackLength === "minimum") {
|
|
1144
|
+
let fallback = length + 1;
|
|
1145
|
+
while (node.value.includes(getExpectedFence(parsed, fallback))) fallback++;
|
|
1146
|
+
if (parsed.openingFence.text.length === fallback) return true;
|
|
1147
|
+
reportNotPreferred(node, parsed, fallback);
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
if (fallbackLength <= length) return true;
|
|
1151
|
+
if (parsed.openingFence.text.length === fallbackLength) return true;
|
|
1152
|
+
const fallbackExpectedFence = getExpectedFence(parsed, fallbackLength);
|
|
1153
|
+
if (node.value.includes(fallbackExpectedFence)) return true;
|
|
1154
|
+
reportNotPreferred(node, parsed, fallbackLength);
|
|
1155
|
+
return false;
|
|
1156
|
+
}
|
|
1157
|
+
/**
|
|
1158
|
+
* Get the expected fence string for the given length.
|
|
1159
|
+
*/
|
|
1160
|
+
function getExpectedFence(parsed, length) {
|
|
1161
|
+
const fenceChar = parsed.openingFence.text[0];
|
|
1162
|
+
return fenceChar.repeat(Math.max(3, length));
|
|
1163
|
+
}
|
|
1164
|
+
/**
|
|
1165
|
+
* Verify that the opening and closing fence lengths match.
|
|
1166
|
+
*/
|
|
1167
|
+
function verifyClosingFenceLength(node, parsed) {
|
|
1168
|
+
if (parsed.openingFence.text.length === parsed.closingFence.text.length) return true;
|
|
1169
|
+
context.report({
|
|
1170
|
+
node,
|
|
1171
|
+
loc: parsed.closingFence.loc,
|
|
1172
|
+
messageId: "notMatch",
|
|
1173
|
+
fix(fixer) {
|
|
1174
|
+
return [fixer.replaceTextRange(parsed.closingFence.range, parsed.openingFence.text)];
|
|
1175
|
+
}
|
|
1176
|
+
});
|
|
1177
|
+
return false;
|
|
1178
|
+
}
|
|
1179
|
+
return { code(node) {
|
|
1180
|
+
const parsed = parseFencedCodeBlock(sourceCode, node);
|
|
1181
|
+
if (!parsed) return;
|
|
1182
|
+
if (!verifyFenceLength(node, parsed)) return;
|
|
1183
|
+
verifyClosingFenceLength(node, parsed);
|
|
1184
|
+
} };
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
|
|
1188
|
+
//#endregion
|
|
1189
|
+
//#region src/rules/code-fence-style.ts
|
|
1190
|
+
var code_fence_style_default = createRule("code-fence-style", {
|
|
1191
|
+
meta: {
|
|
1192
|
+
type: "layout",
|
|
1193
|
+
docs: {
|
|
1194
|
+
description: "enforce a consistent code fence style (backtick or tilde) in Markdown fenced code blocks.",
|
|
1195
|
+
categories: [],
|
|
1196
|
+
listCategory: "Stylistic"
|
|
1197
|
+
},
|
|
1198
|
+
fixable: "code",
|
|
1199
|
+
hasSuggestions: false,
|
|
1200
|
+
schema: [{
|
|
1201
|
+
type: "object",
|
|
1202
|
+
properties: { style: {
|
|
1203
|
+
type: "string",
|
|
1204
|
+
enum: ["backtick", "tilde"]
|
|
1205
|
+
} },
|
|
1206
|
+
additionalProperties: false
|
|
1207
|
+
}],
|
|
1208
|
+
messages: { useCodeFenceStyle: "Use {{expected}} code fence style instead of {{actual}}." }
|
|
1209
|
+
},
|
|
1210
|
+
create(context) {
|
|
1211
|
+
const sourceCode = context.sourceCode;
|
|
1212
|
+
const styleOption = context.options[0]?.style ?? "backtick";
|
|
1213
|
+
const expectedChar = styleOption === "tilde" ? "~" : "`";
|
|
1214
|
+
return { code(node) {
|
|
1215
|
+
const parsed = parseFencedCodeBlock(sourceCode, node);
|
|
1216
|
+
if (!parsed) return;
|
|
1217
|
+
if (parsed.openingFence.text.includes(expectedChar)) return;
|
|
1218
|
+
const expectedOpeningFence = expectedChar.repeat(Math.max(3, parsed.openingFence.text.length));
|
|
1219
|
+
if (node.value.includes(expectedOpeningFence)) return;
|
|
1220
|
+
context.report({
|
|
1221
|
+
node,
|
|
1222
|
+
loc: parsed.openingFence.loc,
|
|
1223
|
+
data: {
|
|
1224
|
+
expected: expectedOpeningFence,
|
|
1225
|
+
actual: parsed.openingFence.text
|
|
1226
|
+
},
|
|
1227
|
+
messageId: "useCodeFenceStyle",
|
|
1228
|
+
fix(fixer) {
|
|
1229
|
+
return [fixer.replaceTextRange(parsed.openingFence.range, expectedOpeningFence), fixer.replaceTextRange(parsed.closingFence.range, expectedChar.repeat(Math.max(3, parsed.closingFence.text.length)))];
|
|
947
1230
|
}
|
|
948
1231
|
});
|
|
949
1232
|
} };
|
|
@@ -6950,6 +7233,8 @@ const rules$1 = [
|
|
|
6950
7233
|
blockquote_marker_alignment_default,
|
|
6951
7234
|
bullet_list_marker_style_default,
|
|
6952
7235
|
canonical_code_block_language_default,
|
|
7236
|
+
code_fence_length_default,
|
|
7237
|
+
code_fence_style_default,
|
|
6953
7238
|
definitions_last_default,
|
|
6954
7239
|
emoji_notation_default,
|
|
6955
7240
|
emphasis_delimiters_style_default,
|
|
@@ -7020,7 +7305,7 @@ __export(meta_exports, {
|
|
|
7020
7305
|
version: () => version
|
|
7021
7306
|
});
|
|
7022
7307
|
const name = "eslint-plugin-markdown-preferences";
|
|
7023
|
-
const version = "0.
|
|
7308
|
+
const version = "0.20.0";
|
|
7024
7309
|
|
|
7025
7310
|
//#endregion
|
|
7026
7311
|
//#region src/index.ts
|