@tony.ganchev/eslint-plugin-header 3.4.2 → 3.4.3
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 +51 -4
- package/lib/rules/header.js +61 -10
- package/package.json +9 -6
- package/types/lib/rules/header.d.ts +1 -1
- package/types/lib/rules/header.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
The native ESLint 9/10 standard header-validating plugin. A zero-bloat, drop-in
|
|
8
8
|
replacement for [eslint-plugin-header](https://github.com/Stuk/eslint-plugin-header)
|
|
9
9
|
with first-class Flat Config & TypeScript support. Auto-fix copyright, license,
|
|
10
|
-
and banner comments in JavaScript, TypeScript, Vue, Svelte, CSS, HTML, and
|
|
10
|
+
and banner comments in JavaScript, TypeScript, Vue, Svelte, CSS, HTML, YAML, and
|
|
11
11
|
Markdown files. Supports _oxlint_.
|
|
12
12
|
|
|
13
13
|
## Table of Contents
|
|
@@ -33,6 +33,7 @@ Markdown files. Supports _oxlint_.
|
|
|
33
33
|
7. [Linting Vue](#linting-vue)
|
|
34
34
|
8. [Linting Svelte](#linting-svelte)
|
|
35
35
|
9. [Linting Markdown](#linting-markdown)
|
|
36
|
+
10. [Linting YAML](#linting-yaml)
|
|
36
37
|
5. [Comparison to Alternatives](#comparison-to-alternatives)
|
|
37
38
|
1. [Compared to eslint-plugin-headers](#compared-to-eslint-plugin-headers)
|
|
38
39
|
1. [Health Scans](#health-scans)
|
|
@@ -134,9 +135,10 @@ statements. Smoke tests cover this support as well.
|
|
|
134
135
|
### Languages
|
|
135
136
|
|
|
136
137
|
Currently the plugin supports linting copyright headers in JavaScript,
|
|
137
|
-
TypeScript and their JSX / TSX flavors; Vue, Svelte, CSS, HTML, and
|
|
138
|
-
files. As mentioned in the previous sections, not all languages are
|
|
139
|
-
for oxlint or ESLint older than 9. Refer to the table below for more
|
|
138
|
+
TypeScript and their JSX / TSX flavors; Vue, Svelte, CSS, HTML, YAML, and
|
|
139
|
+
Markdown files. As mentioned in the previous sections, not all languages are
|
|
140
|
+
supported for oxlint or ESLint older than 9. Refer to the table below for more
|
|
141
|
+
details.
|
|
140
142
|
|
|
141
143
|
| Language | ESLint 7 / 8 | ESLint 9 / 10 | oxlint |
|
|
142
144
|
|------------|---------------|---------------|--------|
|
|
@@ -148,6 +150,7 @@ for oxlint or ESLint older than 9. Refer to the table below for more details.
|
|
|
148
150
|
| Svelte | ✅ Yes | ✅ Yes | ❌ No |
|
|
149
151
|
| CSS | ❌ No | ✅ Yes | ❌ No |
|
|
150
152
|
| HTML | ❌ No | ✅ Yes | ❌ No |
|
|
153
|
+
| YAML | ❌ No | ✅ Yes | ❌ No |
|
|
151
154
|
| Markdown | ❌ No | ✅ Yes | ❌ No |
|
|
152
155
|
|
|
153
156
|
## Usage
|
|
@@ -1488,6 +1491,50 @@ Markdown (`**/*.md/*.js`). Same applies to `*.ts`, `*.jsx`, `*.tsx`, etc.
|
|
|
1488
1491
|
|
|
1489
1492
|
As with CSS, only block comments are supported - no line- or shebang comments.
|
|
1490
1493
|
|
|
1494
|
+
### Linting YAML
|
|
1495
|
+
|
|
1496
|
+
The rule supports linting copyright notices in YAML files. The rule works with
|
|
1497
|
+
the _eslint-plugin-yml_ plugin and its parser.
|
|
1498
|
+
|
|
1499
|
+
Similar to CSS and Markdown, all you need to do to turn on header validation is
|
|
1500
|
+
to configure the _eslint-plugin-yml_ plugin, the correct language, and the rule:
|
|
1501
|
+
|
|
1502
|
+
```ts
|
|
1503
|
+
import header from "@tony.ganchev/eslint-plugin-header";
|
|
1504
|
+
import yml from "eslint-plugin-yml";
|
|
1505
|
+
|
|
1506
|
+
export default [
|
|
1507
|
+
{
|
|
1508
|
+
files: ["**/*.yaml", "**/*.yml"],
|
|
1509
|
+
plugins: {
|
|
1510
|
+
"@tony.ganchev": header,
|
|
1511
|
+
yml
|
|
1512
|
+
},
|
|
1513
|
+
language: "yml/yaml",
|
|
1514
|
+
rules: {
|
|
1515
|
+
"@tony.ganchev/header": [
|
|
1516
|
+
"error",
|
|
1517
|
+
{
|
|
1518
|
+
header: {
|
|
1519
|
+
commentType: "line",
|
|
1520
|
+
lines: [" Copyright 2025 "]
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
]
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
];
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
```yaml
|
|
1530
|
+
# Copyright 2025
|
|
1531
|
+
|
|
1532
|
+
foo: bar
|
|
1533
|
+
```
|
|
1534
|
+
|
|
1535
|
+
As YAML only supports line comments, be sure to use `commentType: "line"`. If
|
|
1536
|
+
`commentType: "block"` is used, the rule will report a violation.
|
|
1537
|
+
|
|
1491
1538
|
## Comparison to Alternatives
|
|
1492
1539
|
|
|
1493
1540
|
A number of projects have been aiming to solve problems similar to
|
package/lib/rules/header.js
CHANGED
|
@@ -206,7 +206,7 @@ function match(actual, expected) {
|
|
|
206
206
|
|
|
207
207
|
/**
|
|
208
208
|
* @typedef {object} Language
|
|
209
|
-
* @property { BlockComment } blockComment The tokens delimiting a block
|
|
209
|
+
* @property { BlockComment } [blockComment] The tokens delimiting a block
|
|
210
210
|
* comment.
|
|
211
211
|
* @property { LineComment } [lineComment] The tokens delimiting a line comment
|
|
212
212
|
* if such is allowed.
|
|
@@ -348,7 +348,8 @@ function getLeadingComments(sourceCode, language) {
|
|
|
348
348
|
*/
|
|
349
349
|
function genCommentBody(commentType, textArray, eol, language) {
|
|
350
350
|
if (commentType === commentTypeOptions.block) {
|
|
351
|
-
|
|
351
|
+
const blockComment = /** @type {BlockComment} */ (language.blockComment);
|
|
352
|
+
return blockComment.startDelimiter + textArray.join(eol) + blockComment.endDelimiter;
|
|
352
353
|
} else {
|
|
353
354
|
const lineStartDelimiter = /** @type {LineComment} */ (language.lineComment).startDelimiter;
|
|
354
355
|
// We need one trailing EOL on line comments to ensure the fixed source
|
|
@@ -413,8 +414,15 @@ function genPrependFixer(commentType, sourceCode, headerLines, eol, numNewlines,
|
|
|
413
414
|
newHeader = eol + newHeader;
|
|
414
415
|
}
|
|
415
416
|
}
|
|
416
|
-
const
|
|
417
|
-
const
|
|
417
|
+
const existingSourceCodeFromInsertPos = sourceCode.text.substring(insertPos);
|
|
418
|
+
const numEmptyLines = leadingEmptyLines(existingSourceCodeFromInsertPos);
|
|
419
|
+
const additionalEmptyLines = Math.max(numNewlines - numEmptyLines,
|
|
420
|
+
(commentType === commentTypeOptions.line
|
|
421
|
+
&& existingSourceCodeFromInsertPos.startsWith(
|
|
422
|
+
/** @type {{ startDelimiter: string }} */(language.lineComment).startDelimiter))
|
|
423
|
+
? 1
|
|
424
|
+
: 0
|
|
425
|
+
);
|
|
418
426
|
newHeader += eol.repeat(additionalEmptyLines);
|
|
419
427
|
return fixer.insertTextBeforeRange(
|
|
420
428
|
[insertPos, insertPos /* don't care */],
|
|
@@ -833,7 +841,7 @@ class CommentMatcher {
|
|
|
833
841
|
}
|
|
834
842
|
}
|
|
835
843
|
|
|
836
|
-
const commentRange = leadingComments[
|
|
844
|
+
const commentRange = leadingComments[leadingComments.length - 1].range;
|
|
837
845
|
const actualLeadingEmptyLines = leadingEmptyLines(sourceCode.text.substring(commentRange[1]));
|
|
838
846
|
const missingEmptyLines = this.numLines - actualLeadingEmptyLines;
|
|
839
847
|
if (missingEmptyLines > 0) {
|
|
@@ -849,6 +857,7 @@ class CommentMatcher {
|
|
|
849
857
|
|
|
850
858
|
return null;
|
|
851
859
|
}
|
|
860
|
+
|
|
852
861
|
// if block comment pattern has more than 1 line, we also split the
|
|
853
862
|
// comment
|
|
854
863
|
let leadingLines = [leadingComments[0].value];
|
|
@@ -862,8 +871,9 @@ class CommentMatcher {
|
|
|
862
871
|
let errorMessageData;
|
|
863
872
|
/** @type {null | SourceLocation} */
|
|
864
873
|
let errorMessageLoc = null;
|
|
865
|
-
const
|
|
866
|
-
const
|
|
874
|
+
const blockComment = /** @type {BlockComment} */(this.language.blockComment);
|
|
875
|
+
const blockStartLen = blockComment.startDelimiter.length;
|
|
876
|
+
const blockEndLen = blockComment.endDelimiter.length;
|
|
867
877
|
|
|
868
878
|
for (let i = 0; i < this.headerLines.length; i++) {
|
|
869
879
|
if (leadingLines.length - 1 < i) {
|
|
@@ -1017,7 +1027,8 @@ const headerRule = {
|
|
|
1017
1027
|
// messages only applicable to header validation.
|
|
1018
1028
|
missingHeader: "missing header",
|
|
1019
1029
|
noNewlineAfterHeader: "not enough newlines after header: expected: {{expected}}, actual: {{actual}}",
|
|
1020
|
-
|
|
1030
|
+
unsupportedBlockHeader: "block-comment header configured but not supported for this language",
|
|
1031
|
+
unsupportedLineHeader: "line-comment header configured but not supported for this language"
|
|
1021
1032
|
}
|
|
1022
1033
|
},
|
|
1023
1034
|
|
|
@@ -1030,6 +1041,7 @@ const headerRule = {
|
|
|
1030
1041
|
|
|
1031
1042
|
const newStyleOptions = transformLegacyOptions(/** @type {AllHeaderOptions} */(context.options));
|
|
1032
1043
|
const sourceCode = contextSourceCode(context);
|
|
1044
|
+
// console.log(sourceCode);
|
|
1033
1045
|
|
|
1034
1046
|
/**
|
|
1035
1047
|
* Hooks into the processing of the overall script node to do the
|
|
@@ -1051,7 +1063,7 @@ const headerRule = {
|
|
|
1051
1063
|
});
|
|
1052
1064
|
const numLines = /** @type {number} */ (options.trailingEmptyLines?.minimum);
|
|
1053
1065
|
|
|
1054
|
-
if (!language.lineComment &&
|
|
1066
|
+
if (!language.lineComment && header.commentType === "line") {
|
|
1055
1067
|
context.report({
|
|
1056
1068
|
messageId: "unsupportedLineHeader",
|
|
1057
1069
|
loc: {
|
|
@@ -1062,6 +1074,17 @@ const headerRule = {
|
|
|
1062
1074
|
return;
|
|
1063
1075
|
}
|
|
1064
1076
|
|
|
1077
|
+
if (!language.blockComment && header.commentType === "block") {
|
|
1078
|
+
context.report({
|
|
1079
|
+
messageId: "unsupportedBlockHeader",
|
|
1080
|
+
loc: {
|
|
1081
|
+
start: sourceCode.getLocFromIndex(0),
|
|
1082
|
+
end: sourceCode.getLocFromIndex(1),
|
|
1083
|
+
},
|
|
1084
|
+
});
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1065
1088
|
const headerMatcher = new CommentMatcher(header, eol, numLines, language);
|
|
1066
1089
|
const allowedLeadingComments = /** @type {LeadingComments} */ (options.leadingComments).comments;
|
|
1067
1090
|
const allowedCommentsMatchers =
|
|
@@ -1205,6 +1228,13 @@ const headerRule = {
|
|
|
1205
1228
|
}
|
|
1206
1229
|
};
|
|
1207
1230
|
|
|
1231
|
+
/** @type {Language} */
|
|
1232
|
+
const yamlStyleLanguage = {
|
|
1233
|
+
lineComment: {
|
|
1234
|
+
startDelimiter: "#"
|
|
1235
|
+
}
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1208
1238
|
/**
|
|
1209
1239
|
* Tests if the source code was parsed by _@html-eslint/eslint-plugin_'s
|
|
1210
1240
|
* parser.
|
|
@@ -1243,8 +1273,29 @@ const headerRule = {
|
|
|
1243
1273
|
return isHtmlParser() || isVueParser() || isSvelteParser();
|
|
1244
1274
|
};
|
|
1245
1275
|
|
|
1276
|
+
/**
|
|
1277
|
+
* Tests if the source code was parsed by _yaml-eslint-parser_.
|
|
1278
|
+
* @returns {boolean} `true` if the source code is parsed by the parser.
|
|
1279
|
+
*/
|
|
1280
|
+
function isYamlStyleLanguage() {
|
|
1281
|
+
// YAML parser has distinct identity from JS parset but also has an
|
|
1282
|
+
// identifier in its `parserServices` section and that check is
|
|
1283
|
+
// faster.
|
|
1284
|
+
return sourceCode.parserServices.isYAML;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
const programLanguageSearchMap = [
|
|
1288
|
+
{ test: isHtmlStyleLanguage, language: htmlStyleLanguage },
|
|
1289
|
+
{ test: isYamlStyleLanguage, language: yamlStyleLanguage },
|
|
1290
|
+
{ test: () => true, language: jsStyleLanguage }
|
|
1291
|
+
];
|
|
1292
|
+
|
|
1246
1293
|
const hooks = {
|
|
1247
|
-
Program: () => onRootNode(
|
|
1294
|
+
Program: () => onRootNode(
|
|
1295
|
+
/** @type {{ test: () => boolean, language: Language}} */(
|
|
1296
|
+
programLanguageSearchMap.find((item) => item.test())
|
|
1297
|
+
).language
|
|
1298
|
+
),
|
|
1248
1299
|
StyleSheet: () => onRootNode(cssStyleLanguage),
|
|
1249
1300
|
// eslint/markdown
|
|
1250
1301
|
root: () => onRootNode(htmlStyleLanguage),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tony.ganchev/eslint-plugin-header",
|
|
3
|
-
"version": "3.4.
|
|
4
|
-
"description": "The native ESLint 9/10 header plugin. A zero-bloat, drop-in replacement for 'eslint-plugin-header' with first-class Flat Config & TypeScript support. Auto-fix Copyright, License, and banner comments in JavaScript, TypeScript, Vue, Svelte, CSS, HTML, and Markdown files. Supports oxlint.",
|
|
3
|
+
"version": "3.4.3",
|
|
4
|
+
"description": "The native ESLint 9/10 header plugin. A zero-bloat, drop-in replacement for 'eslint-plugin-header' with first-class Flat Config & TypeScript support. Auto-fix Copyright, License, and banner comments in JavaScript, TypeScript, Vue, Svelte, CSS, HTML, YAML, and Markdown files. Supports oxlint.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -21,25 +21,26 @@
|
|
|
21
21
|
"@eslint/core": "^1.2.0",
|
|
22
22
|
"@eslint/css": "^1.1.0",
|
|
23
23
|
"@eslint/js": "^10.0.1",
|
|
24
|
-
"@eslint/markdown": "^
|
|
24
|
+
"@eslint/markdown": "^8.0.1",
|
|
25
25
|
"@html-eslint/eslint-plugin": "^0.58.1",
|
|
26
26
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
27
27
|
"@types/estree": "^1.0.8",
|
|
28
28
|
"@types/json-schema": "^7.0.15",
|
|
29
29
|
"@types/node": "^25.5.2",
|
|
30
30
|
"c8": "^11.0.0",
|
|
31
|
-
"eslint": "^10.
|
|
31
|
+
"eslint": "^10.2.0",
|
|
32
32
|
"eslint-plugin-eslint-plugin": "^7.3.2",
|
|
33
33
|
"eslint-plugin-jsdoc": "^62.9.0",
|
|
34
34
|
"eslint-plugin-n": "^17.24.0",
|
|
35
35
|
"eslint-plugin-svelte": "^3.17.0",
|
|
36
36
|
"eslint-plugin-vue": "^10.8.0",
|
|
37
|
+
"eslint-plugin-yml": "^3.3.1",
|
|
37
38
|
"globals": "^17.4.0",
|
|
38
39
|
"markdownlint-cli": "^0.48.0",
|
|
39
40
|
"mocha": "12.0.0-beta-10",
|
|
40
41
|
"svelte-eslint-parser": "^1.6.0",
|
|
41
42
|
"testdouble": "^3.20.2",
|
|
42
|
-
"typescript": "^
|
|
43
|
+
"typescript": "^6.0.2",
|
|
43
44
|
"typescript-eslint": "^8.58.0",
|
|
44
45
|
"vue-eslint-parser": "^10.4.0"
|
|
45
46
|
},
|
|
@@ -84,7 +85,9 @@
|
|
|
84
85
|
"typescript",
|
|
85
86
|
"vue",
|
|
86
87
|
"vuejs",
|
|
87
|
-
"vue-js"
|
|
88
|
+
"vue-js",
|
|
89
|
+
"yaml",
|
|
90
|
+
"yml"
|
|
88
91
|
],
|
|
89
92
|
"repository": {
|
|
90
93
|
"type": "git",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../lib/rules/header.js"],"names":[],"mappings":"2BAsCa,iBAAiB;0BACjB,gBAAgB;wBAChB,cAAc;0BACd,gBAAgB;;;;;yBAIhB,IAAI,GAAG,MAAM;;;;;;;;;;aAOZ,MAAM,GAAG,MAAM;;;;;;;;;;;;yBAOhB,MAAM,GAAG,MAAM,GAAG,iBAAiB;;;;;0BAGnC,UAAU,GAAG,UAAU,EAAE;;;;;;+BAEzB,IAAI,GAAG,MAAM,GAAG,SAAS;;;;;6BAGzB;IAAE,WAAW,CAAC,EAAE,gBAAgB,CAAA;CAAE;;;;;0BAElC,OAAO,GAAG,MAAM;;;;;;;;;;UAOf,MAAM;;;;;;;;;;;;;;;iBASN,WAAW;;;;;;WACX,UAAU,EAAE;;;;;;;;;;;;cAQZ,CAAC,eAAe,GAAG,YAAY,CAAC,EAAE;;;;;;;;;;;;;;;;;;YAclC,eAAe,GAAG,YAAY;;;;;;;;;;;;;;;;;4BAU/B,4BAA4B,GAAG,cAAc;oCAK7C,CAAC,QAAQ,EAAE,MAAM,CAAC;4CAClB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC;iCAE5C,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC;yCACvC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC;yCAEjE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC;iDAEzD,CACV,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,CACvB;;;;+BACS,CAAC,aAAa,CAAC,GACvB,qBAAqB,GACrB,6BAA6B,GAC7B,kBAAkB,GAClB,0BAA0B,GAC1B,0BAA0B,GAC1B,kCAAkC;;;;;+BAK1B,iBAAiB,gBAAgB,CAAC;oBAiClC,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;UAKf,SAAS,GAAG,OAAO,GAAG,MAAM;;;;WAC5B,MAAM;;;;WACN,KAAK;;;;SACL,cAAc;;;;;;;oBAKd,MAAM;;;;;kBAEN,MAAM;;;;;;;oBAMN,MAAM
|
|
1
|
+
{"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../../lib/rules/header.js"],"names":[],"mappings":"2BAsCa,iBAAiB;0BACjB,gBAAgB;wBAChB,cAAc;0BACd,gBAAgB;;;;;yBAIhB,IAAI,GAAG,MAAM;;;;;;;;;;aAOZ,MAAM,GAAG,MAAM;;;;;;;;;;;;yBAOhB,MAAM,GAAG,MAAM,GAAG,iBAAiB;;;;;0BAGnC,UAAU,GAAG,UAAU,EAAE;;;;;;+BAEzB,IAAI,GAAG,MAAM,GAAG,SAAS;;;;;6BAGzB;IAAE,WAAW,CAAC,EAAE,gBAAgB,CAAA;CAAE;;;;;0BAElC,OAAO,GAAG,MAAM;;;;;;;;;;UAOf,MAAM;;;;;;;;;;;;;;;iBASN,WAAW;;;;;;WACX,UAAU,EAAE;;;;;;;;;;;;cAQZ,CAAC,eAAe,GAAG,YAAY,CAAC,EAAE;;;;;;;;;;;;;;;;;;YAclC,eAAe,GAAG,YAAY;;;;;;;;;;;;;;;;;4BAU/B,4BAA4B,GAAG,cAAc;oCAK7C,CAAC,QAAQ,EAAE,MAAM,CAAC;4CAClB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC;iCAE5C,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC;yCACvC,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC;yCAEjE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,CAAC;iDAEzD,CACV,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,CACvB;;;;+BACS,CAAC,aAAa,CAAC,GACvB,qBAAqB,GACrB,6BAA6B,GAC7B,kBAAkB,GAClB,0BAA0B,GAC1B,0BAA0B,GAC1B,kCAAkC;;;;;+BAK1B,iBAAiB,gBAAgB,CAAC;oBAiClC,CAAC,MAAM,EAAE,MAAM,CAAC;;;;;UAKf,SAAS,GAAG,OAAO,GAAG,MAAM;;;;WAC5B,MAAM;;;;WACN,KAAK;;;;SACL,cAAc;;;;;;;oBAKd,MAAM;;;;;kBAEN,MAAM;;;;;;;oBAMN,MAAM;;;;;;;;;;;;;;;;;;;AAmxBpB,8BAA8B;AAC9B,0BADW,eAAe,CA2TxB;AA3sBF,2EAA2E;AAC3E,+BADW,MAAM,CAAC,MAAM,EAAE;IAAE,WAAW,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAC,CAAC,CAC3C;0BA3iBkC,QAAQ;4BAAR,QAAQ;oCAC1B,QAAQ"}
|