@saasmakers/eslint 1.0.24 → 1.0.26
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/dist/eslint.config.cjs +1 -1
- package/dist/eslint.config.d.cts +1 -1
- package/dist/eslint.config.d.mts +1 -1
- package/dist/eslint.config.d.ts +1 -1
- package/dist/eslint.config.mjs +1 -1
- package/dist/index.cjs +140 -10
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +140 -10
- package/package.json +1 -1
package/dist/eslint.config.cjs
CHANGED
|
@@ -10764,7 +10764,7 @@ const eslint_config = antfu__default(
|
|
|
10764
10764
|
test: false,
|
|
10765
10765
|
typescript: true,
|
|
10766
10766
|
unocss: true,
|
|
10767
|
-
vue:
|
|
10767
|
+
vue: true
|
|
10768
10768
|
},
|
|
10769
10769
|
// Antfu changes (keep them minimal)
|
|
10770
10770
|
{
|
package/dist/eslint.config.d.cts
CHANGED
package/dist/eslint.config.d.mts
CHANGED
package/dist/eslint.config.d.ts
CHANGED
package/dist/eslint.config.mjs
CHANGED
|
@@ -10738,7 +10738,7 @@ const eslint_config = antfu(
|
|
|
10738
10738
|
test: false,
|
|
10739
10739
|
typescript: true,
|
|
10740
10740
|
unocss: true,
|
|
10741
|
-
vue:
|
|
10741
|
+
vue: true
|
|
10742
10742
|
},
|
|
10743
10743
|
// Antfu changes (keep them minimal)
|
|
10744
10744
|
{
|
package/dist/index.cjs
CHANGED
|
@@ -810,9 +810,61 @@ const propsPrefixRegex = /\$?props\.(\w+)/g;
|
|
|
810
810
|
const trueAttributeRegex = /(?<![\w-]):?(?!aria-)([a-z0-9-]+)="true"/gi;
|
|
811
811
|
const eventHandlerRegex = /(?:@|v-on:)[^\s="'<>/]+\s*=\s*"([^"]*)"/g;
|
|
812
812
|
const bareIdentifierRegex = /^[a-z_$][\w$]*$/i;
|
|
813
|
+
const classBindingRegex = /(?<![\w-])(?::|v-bind:)class\s*=\s*"([^"]*)"/g;
|
|
814
|
+
const simpleTernaryRegex = /^([^?]+)\?\s*'([^']*)'\s*:\s*'([^']*)'$/;
|
|
813
815
|
const singleCallRegex = /^([a-z_$][\w$]*)\s*\(.*\)$/is;
|
|
814
816
|
const onPrefixRegex = /^on[A-Z]/;
|
|
815
817
|
const exemptCallees = /* @__PURE__ */ new Set(["$emit", "emit"]);
|
|
818
|
+
function splitClassObjectProperties(body) {
|
|
819
|
+
const properties = [];
|
|
820
|
+
let depth = 0;
|
|
821
|
+
let quote;
|
|
822
|
+
let current = "";
|
|
823
|
+
for (let index = 0; index < body.length; index++) {
|
|
824
|
+
const char = body[index];
|
|
825
|
+
if (quote !== void 0) {
|
|
826
|
+
current += char;
|
|
827
|
+
if (char === quote && body[index - 1] !== "\\") {
|
|
828
|
+
quote = void 0;
|
|
829
|
+
}
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
833
|
+
quote = char;
|
|
834
|
+
current += char;
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
if (char === "(" || char === "[" || char === "{") {
|
|
838
|
+
depth++;
|
|
839
|
+
} else if (char === ")" || char === "]" || char === "}") {
|
|
840
|
+
depth--;
|
|
841
|
+
}
|
|
842
|
+
if (char === "," && depth === 0) {
|
|
843
|
+
const property2 = current.trim();
|
|
844
|
+
if (property2 !== "") {
|
|
845
|
+
properties.push(property2);
|
|
846
|
+
}
|
|
847
|
+
current = "";
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
current += char;
|
|
851
|
+
}
|
|
852
|
+
const property = current.trim();
|
|
853
|
+
if (property !== "") {
|
|
854
|
+
properties.push(property);
|
|
855
|
+
}
|
|
856
|
+
return properties;
|
|
857
|
+
}
|
|
858
|
+
function buildClassObjectText(properties, baseIndent) {
|
|
859
|
+
if (properties.length <= 1) {
|
|
860
|
+
return `{ ${properties.join(", ")} }`;
|
|
861
|
+
}
|
|
862
|
+
const propertyIndent = `${baseIndent} `;
|
|
863
|
+
const lines = properties.map((property) => `${propertyIndent}${property},`);
|
|
864
|
+
return `{
|
|
865
|
+
${lines.join("\n")}
|
|
866
|
+
${baseIndent}}`;
|
|
867
|
+
}
|
|
816
868
|
const dollarTPatterns = [
|
|
817
869
|
{
|
|
818
870
|
pattern: / \$t\(/g,
|
|
@@ -838,12 +890,16 @@ const dollarTPatterns = [
|
|
|
838
890
|
const rule = {
|
|
839
891
|
defaultOptions: [],
|
|
840
892
|
meta: {
|
|
841
|
-
docs: { description: 'Format Vue templates: strip unnecessary props/$props prefixes, redundant ="true" attributes (except aria- attributes), enforce t() over $t(),
|
|
893
|
+
docs: { description: 'Format Vue templates: strip unnecessary props/$props prefixes, redundant ="true" attributes (except aria- attributes), enforce t() over $t(), require "on" prefix on named event listener handlers, disallow inline event handler expressions, require object syntax for dynamic :class bindings, and keep single-property :class objects on one line while spreading multi-property objects across lines' },
|
|
842
894
|
fixable: "code",
|
|
843
895
|
messages: {
|
|
896
|
+
dynamicClassMustBeObject: `Dynamic :class binding must use object syntax (e.g. :class="{ 'foo': condition }"). Ternaries, arrays, strings and bare variables are not allowed.`,
|
|
897
|
+
eventHandlerMustBeFunction: 'Event listener must reference a named handler function (e.g. @click="onClose") or call one (e.g. @click="onClose($event)"). Inline expressions are not allowed.',
|
|
898
|
+
multilineClassObject: "Dynamic :class object with multiple properties must span multiple lines, one property per line.",
|
|
844
899
|
noPropsPrefix: "Unnecessary props/$props prefix in template. Props are automatically available in template scope.",
|
|
845
900
|
prefixEventHandlerWithOn: 'Event listener handler "{{name}}" must be prefixed with "on" (e.g. @click="onClick").',
|
|
846
901
|
removeTrueAttribute: 'Unnecessary ="true" attribute. Use the attribute name directly instead.',
|
|
902
|
+
singleLineClassObject: "Dynamic :class object with a single property must stay on one line.",
|
|
847
903
|
useT: "Use t() instead of $t() in Vue templates as it does not work with <i18n> tags."
|
|
848
904
|
},
|
|
849
905
|
schema: [],
|
|
@@ -922,28 +978,102 @@ const rule = {
|
|
|
922
978
|
while (eventHandlerMatch !== null) {
|
|
923
979
|
const handlerValue = eventHandlerMatch[1].trim();
|
|
924
980
|
let handlerName;
|
|
981
|
+
let isBareIdentifier = false;
|
|
982
|
+
let isSingleCall = false;
|
|
925
983
|
if (bareIdentifierRegex.test(handlerValue)) {
|
|
926
984
|
handlerName = handlerValue;
|
|
985
|
+
isBareIdentifier = true;
|
|
927
986
|
} else {
|
|
928
987
|
const singleCallMatch = singleCallRegex.exec(handlerValue);
|
|
929
988
|
if (singleCallMatch) {
|
|
930
989
|
handlerName = singleCallMatch[1];
|
|
990
|
+
isSingleCall = true;
|
|
931
991
|
}
|
|
932
992
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
993
|
+
const valueStart = templateStartIndex + eventHandlerMatch.index + eventHandlerMatch[0].length - 1 - eventHandlerMatch[1].length;
|
|
994
|
+
const valueEnd = valueStart + eventHandlerMatch[1].length;
|
|
995
|
+
const handlerLoc = {
|
|
996
|
+
end: sourceCode.getLocFromIndex(valueEnd),
|
|
997
|
+
start: sourceCode.getLocFromIndex(valueStart)
|
|
998
|
+
};
|
|
999
|
+
if (isBareIdentifier || isSingleCall) {
|
|
1000
|
+
if (handlerName && !exemptCallees.has(handlerName) && !onPrefixRegex.test(handlerName)) {
|
|
1001
|
+
context.report({
|
|
1002
|
+
data: { name: handlerName },
|
|
1003
|
+
loc: handlerLoc,
|
|
1004
|
+
messageId: "prefixEventHandlerWithOn"
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
} else {
|
|
936
1008
|
context.report({
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
end: sourceCode.getLocFromIndex(valueEnd),
|
|
940
|
-
start: sourceCode.getLocFromIndex(valueStart)
|
|
941
|
-
},
|
|
942
|
-
messageId: "prefixEventHandlerWithOn"
|
|
1009
|
+
loc: handlerLoc,
|
|
1010
|
+
messageId: "eventHandlerMustBeFunction"
|
|
943
1011
|
});
|
|
944
1012
|
}
|
|
945
1013
|
eventHandlerMatch = eventHandlerRegex.exec(templateContent);
|
|
946
1014
|
}
|
|
1015
|
+
classBindingRegex.lastIndex = 0;
|
|
1016
|
+
let classMatch = classBindingRegex.exec(templateContent);
|
|
1017
|
+
while (classMatch !== null) {
|
|
1018
|
+
const rawValue = classMatch[1];
|
|
1019
|
+
const trimmedValue = rawValue.trim();
|
|
1020
|
+
if (trimmedValue === "") {
|
|
1021
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
const valueStart = templateStartIndex + classMatch.index + classMatch[0].length - 1 - rawValue.length;
|
|
1025
|
+
const lineStart = templateContent.lastIndexOf("\n", classMatch.index) + 1;
|
|
1026
|
+
const baseIndent = (/^[ \t]*/.exec(templateContent.slice(lineStart)) ?? [""])[0];
|
|
1027
|
+
if (trimmedValue.startsWith("{")) {
|
|
1028
|
+
const objectStartInRaw = rawValue.indexOf("{");
|
|
1029
|
+
const objectEndInRaw = rawValue.lastIndexOf("}");
|
|
1030
|
+
if (objectEndInRaw > objectStartInRaw) {
|
|
1031
|
+
const objectText = rawValue.slice(objectStartInRaw, objectEndInRaw + 1);
|
|
1032
|
+
const properties = splitClassObjectProperties(rawValue.slice(objectStartInRaw + 1, objectEndInRaw));
|
|
1033
|
+
const isMultiline = objectText.includes("\n");
|
|
1034
|
+
const shouldBeMultiline = properties.length >= 2;
|
|
1035
|
+
if (properties.length > 0 && shouldBeMultiline !== isMultiline) {
|
|
1036
|
+
const objectStart = valueStart + objectStartInRaw;
|
|
1037
|
+
const objectEnd = valueStart + objectEndInRaw + 1;
|
|
1038
|
+
context.report({
|
|
1039
|
+
fix: (fixer) => fixer.replaceTextRange([objectStart, objectEnd], buildClassObjectText(properties, baseIndent)),
|
|
1040
|
+
loc: {
|
|
1041
|
+
end: sourceCode.getLocFromIndex(objectEnd),
|
|
1042
|
+
start: sourceCode.getLocFromIndex(objectStart)
|
|
1043
|
+
},
|
|
1044
|
+
messageId: shouldBeMultiline ? "multilineClassObject" : "singleLineClassObject"
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
const valueEnd = valueStart + rawValue.length;
|
|
1052
|
+
const ternaryMatch = simpleTernaryRegex.exec(trimmedValue);
|
|
1053
|
+
let fix;
|
|
1054
|
+
if (ternaryMatch) {
|
|
1055
|
+
const condition = ternaryMatch[1].trim();
|
|
1056
|
+
const truthyClass = ternaryMatch[2];
|
|
1057
|
+
const falsyClass = ternaryMatch[3];
|
|
1058
|
+
const entries = [];
|
|
1059
|
+
if (truthyClass !== "") {
|
|
1060
|
+
entries.push(`'${truthyClass}': ${condition}`);
|
|
1061
|
+
}
|
|
1062
|
+
if (falsyClass !== "") {
|
|
1063
|
+
entries.push(`'${falsyClass}': !(${condition})`);
|
|
1064
|
+
}
|
|
1065
|
+
fix = (fixer) => fixer.replaceTextRange([valueStart, valueEnd], buildClassObjectText(entries, baseIndent));
|
|
1066
|
+
}
|
|
1067
|
+
context.report({
|
|
1068
|
+
...fix ? { fix } : {},
|
|
1069
|
+
loc: {
|
|
1070
|
+
end: sourceCode.getLocFromIndex(valueEnd),
|
|
1071
|
+
start: sourceCode.getLocFromIndex(valueStart)
|
|
1072
|
+
},
|
|
1073
|
+
messageId: "dynamicClassMustBeObject"
|
|
1074
|
+
});
|
|
1075
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1076
|
+
}
|
|
947
1077
|
}
|
|
948
1078
|
};
|
|
949
1079
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -5203,7 +5203,7 @@ declare const _default: {
|
|
|
5203
5203
|
locales?: string[];
|
|
5204
5204
|
} | undefined)?], unknown, RuleListener>;
|
|
5205
5205
|
'vue-format-script': RuleModule<"multilineComputed" | "untypedEmits", [], unknown, RuleListener>;
|
|
5206
|
-
'vue-format-template': RuleModule<"noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "useT", [], unknown, RuleListener>;
|
|
5206
|
+
'vue-format-template': RuleModule<"dynamicClassMustBeObject" | "eventHandlerMustBeFunction" | "multilineClassObject" | "noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "singleLineClassObject" | "useT", [], unknown, RuleListener>;
|
|
5207
5207
|
};
|
|
5208
5208
|
};
|
|
5209
5209
|
|
package/dist/index.d.mts
CHANGED
|
@@ -5203,7 +5203,7 @@ declare const _default: {
|
|
|
5203
5203
|
locales?: string[];
|
|
5204
5204
|
} | undefined)?], unknown, RuleListener>;
|
|
5205
5205
|
'vue-format-script': RuleModule<"multilineComputed" | "untypedEmits", [], unknown, RuleListener>;
|
|
5206
|
-
'vue-format-template': RuleModule<"noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "useT", [], unknown, RuleListener>;
|
|
5206
|
+
'vue-format-template': RuleModule<"dynamicClassMustBeObject" | "eventHandlerMustBeFunction" | "multilineClassObject" | "noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "singleLineClassObject" | "useT", [], unknown, RuleListener>;
|
|
5207
5207
|
};
|
|
5208
5208
|
};
|
|
5209
5209
|
|
package/dist/index.d.ts
CHANGED
|
@@ -5203,7 +5203,7 @@ declare const _default: {
|
|
|
5203
5203
|
locales?: string[];
|
|
5204
5204
|
} | undefined)?], unknown, RuleListener>;
|
|
5205
5205
|
'vue-format-script': RuleModule<"multilineComputed" | "untypedEmits", [], unknown, RuleListener>;
|
|
5206
|
-
'vue-format-template': RuleModule<"noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "useT", [], unknown, RuleListener>;
|
|
5206
|
+
'vue-format-template': RuleModule<"dynamicClassMustBeObject" | "eventHandlerMustBeFunction" | "multilineClassObject" | "noPropsPrefix" | "prefixEventHandlerWithOn" | "removeTrueAttribute" | "singleLineClassObject" | "useT", [], unknown, RuleListener>;
|
|
5207
5207
|
};
|
|
5208
5208
|
};
|
|
5209
5209
|
|
package/dist/index.mjs
CHANGED
|
@@ -808,9 +808,61 @@ const propsPrefixRegex = /\$?props\.(\w+)/g;
|
|
|
808
808
|
const trueAttributeRegex = /(?<![\w-]):?(?!aria-)([a-z0-9-]+)="true"/gi;
|
|
809
809
|
const eventHandlerRegex = /(?:@|v-on:)[^\s="'<>/]+\s*=\s*"([^"]*)"/g;
|
|
810
810
|
const bareIdentifierRegex = /^[a-z_$][\w$]*$/i;
|
|
811
|
+
const classBindingRegex = /(?<![\w-])(?::|v-bind:)class\s*=\s*"([^"]*)"/g;
|
|
812
|
+
const simpleTernaryRegex = /^([^?]+)\?\s*'([^']*)'\s*:\s*'([^']*)'$/;
|
|
811
813
|
const singleCallRegex = /^([a-z_$][\w$]*)\s*\(.*\)$/is;
|
|
812
814
|
const onPrefixRegex = /^on[A-Z]/;
|
|
813
815
|
const exemptCallees = /* @__PURE__ */ new Set(["$emit", "emit"]);
|
|
816
|
+
function splitClassObjectProperties(body) {
|
|
817
|
+
const properties = [];
|
|
818
|
+
let depth = 0;
|
|
819
|
+
let quote;
|
|
820
|
+
let current = "";
|
|
821
|
+
for (let index = 0; index < body.length; index++) {
|
|
822
|
+
const char = body[index];
|
|
823
|
+
if (quote !== void 0) {
|
|
824
|
+
current += char;
|
|
825
|
+
if (char === quote && body[index - 1] !== "\\") {
|
|
826
|
+
quote = void 0;
|
|
827
|
+
}
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
831
|
+
quote = char;
|
|
832
|
+
current += char;
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (char === "(" || char === "[" || char === "{") {
|
|
836
|
+
depth++;
|
|
837
|
+
} else if (char === ")" || char === "]" || char === "}") {
|
|
838
|
+
depth--;
|
|
839
|
+
}
|
|
840
|
+
if (char === "," && depth === 0) {
|
|
841
|
+
const property2 = current.trim();
|
|
842
|
+
if (property2 !== "") {
|
|
843
|
+
properties.push(property2);
|
|
844
|
+
}
|
|
845
|
+
current = "";
|
|
846
|
+
continue;
|
|
847
|
+
}
|
|
848
|
+
current += char;
|
|
849
|
+
}
|
|
850
|
+
const property = current.trim();
|
|
851
|
+
if (property !== "") {
|
|
852
|
+
properties.push(property);
|
|
853
|
+
}
|
|
854
|
+
return properties;
|
|
855
|
+
}
|
|
856
|
+
function buildClassObjectText(properties, baseIndent) {
|
|
857
|
+
if (properties.length <= 1) {
|
|
858
|
+
return `{ ${properties.join(", ")} }`;
|
|
859
|
+
}
|
|
860
|
+
const propertyIndent = `${baseIndent} `;
|
|
861
|
+
const lines = properties.map((property) => `${propertyIndent}${property},`);
|
|
862
|
+
return `{
|
|
863
|
+
${lines.join("\n")}
|
|
864
|
+
${baseIndent}}`;
|
|
865
|
+
}
|
|
814
866
|
const dollarTPatterns = [
|
|
815
867
|
{
|
|
816
868
|
pattern: / \$t\(/g,
|
|
@@ -836,12 +888,16 @@ const dollarTPatterns = [
|
|
|
836
888
|
const rule = {
|
|
837
889
|
defaultOptions: [],
|
|
838
890
|
meta: {
|
|
839
|
-
docs: { description: 'Format Vue templates: strip unnecessary props/$props prefixes, redundant ="true" attributes (except aria- attributes), enforce t() over $t(),
|
|
891
|
+
docs: { description: 'Format Vue templates: strip unnecessary props/$props prefixes, redundant ="true" attributes (except aria- attributes), enforce t() over $t(), require "on" prefix on named event listener handlers, disallow inline event handler expressions, require object syntax for dynamic :class bindings, and keep single-property :class objects on one line while spreading multi-property objects across lines' },
|
|
840
892
|
fixable: "code",
|
|
841
893
|
messages: {
|
|
894
|
+
dynamicClassMustBeObject: `Dynamic :class binding must use object syntax (e.g. :class="{ 'foo': condition }"). Ternaries, arrays, strings and bare variables are not allowed.`,
|
|
895
|
+
eventHandlerMustBeFunction: 'Event listener must reference a named handler function (e.g. @click="onClose") or call one (e.g. @click="onClose($event)"). Inline expressions are not allowed.',
|
|
896
|
+
multilineClassObject: "Dynamic :class object with multiple properties must span multiple lines, one property per line.",
|
|
842
897
|
noPropsPrefix: "Unnecessary props/$props prefix in template. Props are automatically available in template scope.",
|
|
843
898
|
prefixEventHandlerWithOn: 'Event listener handler "{{name}}" must be prefixed with "on" (e.g. @click="onClick").',
|
|
844
899
|
removeTrueAttribute: 'Unnecessary ="true" attribute. Use the attribute name directly instead.',
|
|
900
|
+
singleLineClassObject: "Dynamic :class object with a single property must stay on one line.",
|
|
845
901
|
useT: "Use t() instead of $t() in Vue templates as it does not work with <i18n> tags."
|
|
846
902
|
},
|
|
847
903
|
schema: [],
|
|
@@ -920,28 +976,102 @@ const rule = {
|
|
|
920
976
|
while (eventHandlerMatch !== null) {
|
|
921
977
|
const handlerValue = eventHandlerMatch[1].trim();
|
|
922
978
|
let handlerName;
|
|
979
|
+
let isBareIdentifier = false;
|
|
980
|
+
let isSingleCall = false;
|
|
923
981
|
if (bareIdentifierRegex.test(handlerValue)) {
|
|
924
982
|
handlerName = handlerValue;
|
|
983
|
+
isBareIdentifier = true;
|
|
925
984
|
} else {
|
|
926
985
|
const singleCallMatch = singleCallRegex.exec(handlerValue);
|
|
927
986
|
if (singleCallMatch) {
|
|
928
987
|
handlerName = singleCallMatch[1];
|
|
988
|
+
isSingleCall = true;
|
|
929
989
|
}
|
|
930
990
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
991
|
+
const valueStart = templateStartIndex + eventHandlerMatch.index + eventHandlerMatch[0].length - 1 - eventHandlerMatch[1].length;
|
|
992
|
+
const valueEnd = valueStart + eventHandlerMatch[1].length;
|
|
993
|
+
const handlerLoc = {
|
|
994
|
+
end: sourceCode.getLocFromIndex(valueEnd),
|
|
995
|
+
start: sourceCode.getLocFromIndex(valueStart)
|
|
996
|
+
};
|
|
997
|
+
if (isBareIdentifier || isSingleCall) {
|
|
998
|
+
if (handlerName && !exemptCallees.has(handlerName) && !onPrefixRegex.test(handlerName)) {
|
|
999
|
+
context.report({
|
|
1000
|
+
data: { name: handlerName },
|
|
1001
|
+
loc: handlerLoc,
|
|
1002
|
+
messageId: "prefixEventHandlerWithOn"
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
} else {
|
|
934
1006
|
context.report({
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
end: sourceCode.getLocFromIndex(valueEnd),
|
|
938
|
-
start: sourceCode.getLocFromIndex(valueStart)
|
|
939
|
-
},
|
|
940
|
-
messageId: "prefixEventHandlerWithOn"
|
|
1007
|
+
loc: handlerLoc,
|
|
1008
|
+
messageId: "eventHandlerMustBeFunction"
|
|
941
1009
|
});
|
|
942
1010
|
}
|
|
943
1011
|
eventHandlerMatch = eventHandlerRegex.exec(templateContent);
|
|
944
1012
|
}
|
|
1013
|
+
classBindingRegex.lastIndex = 0;
|
|
1014
|
+
let classMatch = classBindingRegex.exec(templateContent);
|
|
1015
|
+
while (classMatch !== null) {
|
|
1016
|
+
const rawValue = classMatch[1];
|
|
1017
|
+
const trimmedValue = rawValue.trim();
|
|
1018
|
+
if (trimmedValue === "") {
|
|
1019
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
1022
|
+
const valueStart = templateStartIndex + classMatch.index + classMatch[0].length - 1 - rawValue.length;
|
|
1023
|
+
const lineStart = templateContent.lastIndexOf("\n", classMatch.index) + 1;
|
|
1024
|
+
const baseIndent = (/^[ \t]*/.exec(templateContent.slice(lineStart)) ?? [""])[0];
|
|
1025
|
+
if (trimmedValue.startsWith("{")) {
|
|
1026
|
+
const objectStartInRaw = rawValue.indexOf("{");
|
|
1027
|
+
const objectEndInRaw = rawValue.lastIndexOf("}");
|
|
1028
|
+
if (objectEndInRaw > objectStartInRaw) {
|
|
1029
|
+
const objectText = rawValue.slice(objectStartInRaw, objectEndInRaw + 1);
|
|
1030
|
+
const properties = splitClassObjectProperties(rawValue.slice(objectStartInRaw + 1, objectEndInRaw));
|
|
1031
|
+
const isMultiline = objectText.includes("\n");
|
|
1032
|
+
const shouldBeMultiline = properties.length >= 2;
|
|
1033
|
+
if (properties.length > 0 && shouldBeMultiline !== isMultiline) {
|
|
1034
|
+
const objectStart = valueStart + objectStartInRaw;
|
|
1035
|
+
const objectEnd = valueStart + objectEndInRaw + 1;
|
|
1036
|
+
context.report({
|
|
1037
|
+
fix: (fixer) => fixer.replaceTextRange([objectStart, objectEnd], buildClassObjectText(properties, baseIndent)),
|
|
1038
|
+
loc: {
|
|
1039
|
+
end: sourceCode.getLocFromIndex(objectEnd),
|
|
1040
|
+
start: sourceCode.getLocFromIndex(objectStart)
|
|
1041
|
+
},
|
|
1042
|
+
messageId: shouldBeMultiline ? "multilineClassObject" : "singleLineClassObject"
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1047
|
+
continue;
|
|
1048
|
+
}
|
|
1049
|
+
const valueEnd = valueStart + rawValue.length;
|
|
1050
|
+
const ternaryMatch = simpleTernaryRegex.exec(trimmedValue);
|
|
1051
|
+
let fix;
|
|
1052
|
+
if (ternaryMatch) {
|
|
1053
|
+
const condition = ternaryMatch[1].trim();
|
|
1054
|
+
const truthyClass = ternaryMatch[2];
|
|
1055
|
+
const falsyClass = ternaryMatch[3];
|
|
1056
|
+
const entries = [];
|
|
1057
|
+
if (truthyClass !== "") {
|
|
1058
|
+
entries.push(`'${truthyClass}': ${condition}`);
|
|
1059
|
+
}
|
|
1060
|
+
if (falsyClass !== "") {
|
|
1061
|
+
entries.push(`'${falsyClass}': !(${condition})`);
|
|
1062
|
+
}
|
|
1063
|
+
fix = (fixer) => fixer.replaceTextRange([valueStart, valueEnd], buildClassObjectText(entries, baseIndent));
|
|
1064
|
+
}
|
|
1065
|
+
context.report({
|
|
1066
|
+
...fix ? { fix } : {},
|
|
1067
|
+
loc: {
|
|
1068
|
+
end: sourceCode.getLocFromIndex(valueEnd),
|
|
1069
|
+
start: sourceCode.getLocFromIndex(valueStart)
|
|
1070
|
+
},
|
|
1071
|
+
messageId: "dynamicClassMustBeObject"
|
|
1072
|
+
});
|
|
1073
|
+
classMatch = classBindingRegex.exec(templateContent);
|
|
1074
|
+
}
|
|
945
1075
|
}
|
|
946
1076
|
};
|
|
947
1077
|
}
|