@angular-eslint/eslint-plugin-template
Advanced tools
Comparing version 0.8.0-beta.4 to 0.8.0-beta.5
@@ -7,2 +7,3 @@ import { RULE_NAME as bananaInBoxRuleName } from './rules/banana-in-box'; | ||
import { RULE_NAME as noPositiveTabindexRuleName } from './rules/no-positive-tabindex'; | ||
import { RULE_NAME as useTrackByFunctionRuleName } from './rules/use-track-by-function'; | ||
declare const _default: { | ||
@@ -58,4 +59,5 @@ configs: { | ||
"no-positive-tabindex": import("@typescript-eslint/experimental-utils/dist/ts-eslint/Rule").RuleModule<"noPositiveTabindex", [], import("@typescript-eslint/experimental-utils/dist/ts-eslint/Rule").RuleListener>; | ||
"use-track-by-function": import("@typescript-eslint/experimental-utils/dist/ts-eslint/Rule").RuleModule<"useTrackByFunction", [], import("@typescript-eslint/experimental-utils/dist/ts-eslint/Rule").RuleListener>; | ||
}; | ||
}; | ||
export default _default; |
@@ -1,1 +0,1 @@ | ||
var e,t=(e=require("typescript"))&&"object"==typeof e&&"default"in e?e.default:e,n=require("@typescript-eslint/experimental-utils");function o(e,t){const n=(e=e.replace(/\r\n/g,"\n")).indexOf(t);return[n,n+t.length]}const s=new Map;var a={"extract-inline-html":{preprocess:function(e,n){const a=[e];if(!function(e,t){return!![".component.ts",".page.ts",".dialog.ts",".modal.ts",".popover.ts",".bottomsheet.ts",".snackbar.ts"].some(e=>t.endsWith(e))||!(!e.includes("Component")||!e.includes("@angular/core"))}(e,n))return a;try{const r=t.createSourceFile(n,e,t.ScriptTarget.Latest,!0),i=r.statements.filter(e=>t.isClassDeclaration(e));if(!i||!i.length)return a;const c=[];for(const e of i)if(e.decorators)for(const n of e.decorators)t.isCallExpression(n.expression)&&t.isIdentifier(n.expression.expression)&&"Component"===n.expression.expression.text&&c.push(n);if(!c||!c.length)return a;const l=[e];let p=0;for(const n of c){if(!t.isDecorator(n)||!t.isCallExpression(n.expression)||1!==n.expression.arguments.length)continue;const a=n.expression.arguments[0];if(!t.isObjectLiteralExpression(a))continue;const i=a.properties.find(e=>e&&e.name&&"template"===e.name.getText());if(a.properties.find(e=>e&&e.name&&"templateUrl"===e.name.getText())||!i)continue;if(!t.isPropertyAssignment(i)||!t.isStringLiteralLike(i.initializer))continue;const c=i.initializer.text,u=o(e,c),d=`inline-template-${++p}.component.html`;s.set(d,{range:u,lineAndCharacter:{start:r.getLineAndCharacterOfPosition(u[0]),end:r.getLineAndCharacterOfPosition(u[1])}}),l.push({text:c,filename:d})}return l}catch(e){return console.log(e),console.error("preprocess: ERROR could not parse @Component() metadata",n),a}},postprocess:function(e,t){const n=e[0];if(1===e.length)return n;const o=e.slice(1);return[...n,...[].concat(...o.map((e,t)=>{const n=`inline-template-${++t}.component.html`,o=s.get(n);return o?e.map(e=>{if(e.line=e.line+o.lineAndCharacter.start.line,e.column=e.column,e.endLine=e.endLine+o.lineAndCharacter.start.line,e.endColumn=e.endColumn,e.fix){const t=o.range[0];e.fix.range=[t+e.fix.range[0],t+e.fix.range[1]]}return e}):[]}))]},supportsAutofix:!0}};const r=n.ESLintUtils.RuleCreator(()=>"");function i(e){if(!e.parserServices||!e.parserServices.defineTemplateBodyVisitor||!e.parserServices.convertNodeSourceSpanToLoc)throw new Error("You have used a rule which requires '@angular-eslint/template-parser' to be used as the 'parser' in your ESLint config.");return e.parserServices}const c=/\[(.*)\]/;var l=r({name:"banana-in-box",meta:{type:"suggestion",docs:{description:"Ensures that the two-way data binding syntax is correct",category:"Best Practices",recommended:"error"},fixable:"code",schema:[],messages:{bananaInBox:"Invalid binding syntax. Use [(expr)] instead"}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({BoundEvent(o){const s=o.name.match(c);if(!s)return;const a=`[(${s[1]})]`,r=t.convertNodeSourceSpanToLoc(o.sourceSpan),i=n.getIndexFromLoc(r.start);e.report({messageId:"bananaInBox",loc:r,fix:e=>e.replaceTextRange([i,"(".length+i+")".length+o.name.length],a)})}})}});const p=["ngForOf","ngIf","ngSwitchCase"],u=["ngSwitchDefault"];var d=r({name:"cyclomatic-complexity",meta:{type:"suggestion",docs:{description:"Checks cyclomatic complexity against a specified limit. It is a quantitative measure of the number of linearly independent paths through a program's source code",category:"Best Practices",recommended:!1},fixable:"code",schema:[{type:"object",properties:{maxComplexity:{type:"number",minimum:1}},additionalProperties:!1}],messages:{cyclomaticComplexity:"The cyclomatic complexity exceeded the defined limit of {{maxComplexity}}. Your template should be refactored."}},defaultOptions:[{maxComplexity:5}],create(e,[t]){let n=0;const o=i(e),{maxComplexity:s}=t,a=t=>{if(n+=1,n<=s)return;const a=o.convertNodeSourceSpanToLoc(t.sourceSpan);e.report({messageId:"cyclomaticComplexity",loc:a,data:{maxComplexity:s}})};return o.defineTemplateBodyVisitor({BoundAttribute(e){p.includes(e.name)&&a(e)},TextAttribute(e){u.includes(e.name)&&a(e)}})}}),m=r({name:"no-autofocus",meta:{type:"suggestion",docs:{description:"Ensure that autofocus attribute is not used",category:"Best Practices",recommended:!1},schema:[],messages:{noAutofocus:"autofocus attribute should not be used, as it reduces usability and accessibility for users"}},defaultOptions:[],create(e){const t=i(e);return t.defineTemplateBodyVisitor({Element(n){if(((e,t)=>e.attributes.find(({name:e})=>"autofocus"===e))(n)){const o=t.convertNodeSourceSpanToLoc(n.startSourceSpan);e.report({messageId:"noAutofocus",loc:o})}}})}});const g=new Set(["$any"]);var f=r({name:"no-call-expression",meta:{type:"suggestion",docs:{description:"Disallows calling expressions in templates, except for output handlers.",category:"Best Practices",recommended:!1},schema:[],messages:{noCallExpression:"Avoid calling expressions in templates."}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({MethodCall(t){if(g.has(t.name)||"BoundEvent"===t.parent.parent.type)return;const o=n.getLocFromIndex(t.sourceSpan.start),s=n.getLocFromIndex(t.sourceSpan.end);e.report({messageId:"noCallExpression",loc:{start:o,end:s}})}})}}),x=r({name:"no-negated-async",meta:{type:"suggestion",docs:{description:"Ensures that strict equality is used when evaluating negations on async pipe output",category:"Best Practices",recommended:"error"},fixable:"code",schema:[],messages:{noNegatedAsync:"Async pipes should not be negated. Use (observable | async) === (false | null | undefined) to check its value instead",noLooseEquality:"Async pipes must use strict equality `===` when comparing with `false`"}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({"BindingPipe[name=async]"(t){if("PrefixNot"!==t.parent.type)if("Binary"!==t.parent.type||"=="!==t.parent.operation);else{const o="Interpolation"===t.parent.parent.type?-1:0,s=n.getLocFromIndex(t.parent.sourceSpan.start+("Interpolation"===t.parent.parent.type?-2:-1)),a=n.getLocFromIndex(t.parent.sourceSpan.end+o);e.report({messageId:"noLooseEquality",loc:{start:s,end:a}})}else{const o="Interpolation"===t.parent.parent.type?-1:0,s=n.getLocFromIndex(t.parent.sourceSpan.start+o),a=n.getLocFromIndex(t.parent.sourceSpan.end+o);e.report({messageId:"noNegatedAsync",loc:{start:s,end:a}})}}})}}),y=r({name:"no-positive-tabindex",meta:{type:"suggestion",docs:{description:"Ensures that the tabindex attribute is not positive",category:"Best Practices",recommended:!1},schema:[],messages:{noPositiveTabindex:"tabindex attribute cannot be positive"}},defaultOptions:[],create(e){const t=i(e);return t.defineTemplateBodyVisitor({Element(n){let o=((e,t)=>{const n=e.attributes.find(e=>"tabindex"===e.name),o=e.inputs.find(e=>"tabindex"===e.name);return n?n.value:o&&o.value.ast?o.value.ast.value:void 0})(n);if(o&&(o=parseInt(o,10),o>0)){const o=t.convertNodeSourceSpanToLoc(n.startSourceSpan);e.report({messageId:"noPositiveTabindex",loc:o})}}})}});module.exports={configs:{all:{extends:"./configs/base.json",rules:{"@angular-eslint/template/banana-in-box":"error","@angular-eslint/template/cyclomatic-complexity":"error","@angular-eslint/template/no-autofocus":"error","@angular-eslint/template/no-call-expression":"error","@angular-eslint/template/no-negated-async":"error","@angular-eslint/template/no-positive-tabindex":"error"}},base:{parser:"@angular-eslint/template-parser",plugins:["@angular-eslint/template"]},recommended:{extends:"./configs/base.json",rules:{"@angular-eslint/template/banana-in-box":"error","@angular-eslint/template/no-negated-async":"error"}},"process-inline-templates":{parser:"@typescript-eslint/parser",parserOptions:{ecmaVersion:2020,sourceType:"module"},plugins:["@angular-eslint/template"],processor:"@angular-eslint/template/extract-inline-html"}},processors:a,rules:{"banana-in-box":l,"cyclomatic-complexity":d,"no-autofocus":m,"no-call-expression":f,"no-negated-async":x,"no-positive-tabindex":y}}; | ||
var e,t=(e=require("typescript"))&&"object"==typeof e&&"default"in e?e.default:e,n=require("@typescript-eslint/experimental-utils");function o(e,t){const n=(e=e.replace(/\r\n/g,"\n")).indexOf(t);return[n,n+t.length]}const r=new Map;var s={"extract-inline-html":{preprocess:function(e,n){const s=[e];if(!function(e,t){return!![".component.ts",".page.ts",".dialog.ts",".modal.ts",".popover.ts",".bottomsheet.ts",".snackbar.ts"].some(e=>t.endsWith(e))||!(!e.includes("Component")||!e.includes("@angular/core"))}(e,n))return s;try{const a=t.createSourceFile(n,e,t.ScriptTarget.Latest,!0),i=a.statements.filter(e=>t.isClassDeclaration(e));if(!i||!i.length)return s;const c=[];for(const e of i)if(e.decorators)for(const n of e.decorators)t.isCallExpression(n.expression)&&t.isIdentifier(n.expression.expression)&&"Component"===n.expression.expression.text&&c.push(n);if(!c||!c.length)return s;const l=[e];let p=0;for(const n of c){if(!t.isDecorator(n)||!t.isCallExpression(n.expression)||1!==n.expression.arguments.length)continue;const s=n.expression.arguments[0];if(!t.isObjectLiteralExpression(s))continue;const i=s.properties.find(e=>e&&e.name&&"template"===e.name.getText());if(s.properties.find(e=>e&&e.name&&"templateUrl"===e.name.getText())||!i)continue;if(!t.isPropertyAssignment(i)||!t.isStringLiteralLike(i.initializer))continue;const c=i.initializer.text,u=o(e,c),d=`inline-template-${++p}.component.html`;r.set(d,{range:u,lineAndCharacter:{start:a.getLineAndCharacterOfPosition(u[0]),end:a.getLineAndCharacterOfPosition(u[1])}}),l.push({text:c,filename:d})}return l}catch(e){return console.log(e),console.error("preprocess: ERROR could not parse @Component() metadata",n),s}},postprocess:function(e,t){const n=e[0];if(1===e.length)return n;const o=e.slice(1);return[...n,...[].concat(...o.map((e,t)=>{const n=`inline-template-${++t}.component.html`,o=r.get(n);return o?e.map(e=>{if(e.line=e.line+o.lineAndCharacter.start.line,e.column=e.column,e.endLine=e.endLine+o.lineAndCharacter.start.line,e.endColumn=e.endColumn,e.fix){const t=o.range[0];e.fix.range=[t+e.fix.range[0],t+e.fix.range[1]]}return e}):[]}))]},supportsAutofix:!0}};const a=n.ESLintUtils.RuleCreator(()=>"");function i(e){if(!e.parserServices||!e.parserServices.defineTemplateBodyVisitor||!e.parserServices.convertNodeSourceSpanToLoc)throw new Error("You have used a rule which requires '@angular-eslint/template-parser' to be used as the 'parser' in your ESLint config.");return e.parserServices}const c=/\[(.*)\]/;var l=a({name:"banana-in-box",meta:{type:"suggestion",docs:{description:"Ensures that the two-way data binding syntax is correct",category:"Best Practices",recommended:"error"},fixable:"code",schema:[],messages:{bananaInBox:"Invalid binding syntax. Use [(expr)] instead"}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({BoundEvent(o){const r=o.name.match(c);if(!r)return;const s=`[(${r[1]})]`,a=t.convertNodeSourceSpanToLoc(o.sourceSpan),i=n.getIndexFromLoc(a.start);e.report({messageId:"bananaInBox",loc:a,fix:e=>e.replaceTextRange([i,"(".length+i+")".length+o.name.length],s)})}})}});const p=["ngForOf","ngIf","ngSwitchCase"],u=["ngSwitchDefault"];var d=a({name:"cyclomatic-complexity",meta:{type:"suggestion",docs:{description:"Checks cyclomatic complexity against a specified limit. It is a quantitative measure of the number of linearly independent paths through a program's source code",category:"Best Practices",recommended:!1},fixable:"code",schema:[{type:"object",properties:{maxComplexity:{type:"number",minimum:1}},additionalProperties:!1}],messages:{cyclomaticComplexity:"The cyclomatic complexity exceeded the defined limit of {{maxComplexity}}. Your template should be refactored."}},defaultOptions:[{maxComplexity:5}],create(e,[t]){let n=0;const o=i(e),{maxComplexity:r}=t,s=t=>{if(n+=1,n<=r)return;const s=o.convertNodeSourceSpanToLoc(t.sourceSpan);e.report({messageId:"cyclomaticComplexity",loc:s,data:{maxComplexity:r}})};return o.defineTemplateBodyVisitor({BoundAttribute(e){p.includes(e.name)&&s(e)},TextAttribute(e){u.includes(e.name)&&s(e)}})}}),m=a({name:"no-autofocus",meta:{type:"suggestion",docs:{description:"Ensure that autofocus attribute is not used",category:"Best Practices",recommended:!1},schema:[],messages:{noAutofocus:"autofocus attribute should not be used, as it reduces usability and accessibility for users"}},defaultOptions:[],create(e){const t=i(e);return t.defineTemplateBodyVisitor({Element(n){if(((e,t)=>e.attributes.find(({name:e})=>"autofocus"===e))(n)){const o=t.convertNodeSourceSpanToLoc(n.startSourceSpan);e.report({messageId:"noAutofocus",loc:o})}}})}});const g=new Set(["$any"]);var f=a({name:"no-call-expression",meta:{type:"suggestion",docs:{description:"Disallows calling expressions in templates, except for output handlers.",category:"Best Practices",recommended:!1},schema:[],messages:{noCallExpression:"Avoid calling expressions in templates."}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({MethodCall(t){if(g.has(t.name)||"BoundEvent"===t.parent.parent.type)return;const o=n.getLocFromIndex(t.sourceSpan.start),r=n.getLocFromIndex(t.sourceSpan.end);e.report({messageId:"noCallExpression",loc:{start:o,end:r}})}})}}),y=a({name:"no-negated-async",meta:{type:"suggestion",docs:{description:"Ensures that strict equality is used when evaluating negations on async pipe output",category:"Best Practices",recommended:"error"},fixable:"code",schema:[],messages:{noNegatedAsync:"Async pipes should not be negated. Use (observable | async) === (false | null | undefined) to check its value instead",noLooseEquality:"Async pipes must use strict equality `===` when comparing with `false`"}},defaultOptions:[],create(e){const t=i(e),n=e.getSourceCode();return t.defineTemplateBodyVisitor({"BindingPipe[name=async]"(t){if("PrefixNot"!==t.parent.type)if("Binary"!==t.parent.type||"=="!==t.parent.operation);else{const o="Interpolation"===t.parent.parent.type?-1:0,r=n.getLocFromIndex(t.parent.sourceSpan.start+("Interpolation"===t.parent.parent.type?-2:-1)),s=n.getLocFromIndex(t.parent.sourceSpan.end+o);e.report({messageId:"noLooseEquality",loc:{start:r,end:s}})}else{const o="Interpolation"===t.parent.parent.type?-1:0,r=n.getLocFromIndex(t.parent.sourceSpan.start+o),s=n.getLocFromIndex(t.parent.sourceSpan.end+o);e.report({messageId:"noNegatedAsync",loc:{start:r,end:s}})}}})}}),x=a({name:"no-positive-tabindex",meta:{type:"suggestion",docs:{description:"Ensures that the tabindex attribute is not positive",category:"Best Practices",recommended:!1},schema:[],messages:{noPositiveTabindex:"tabindex attribute cannot be positive"}},defaultOptions:[],create(e){const t=i(e);return t.defineTemplateBodyVisitor({Element(n){let o=((e,t)=>{const n=e.attributes.find(e=>"tabindex"===e.name),o=e.inputs.find(e=>"tabindex"===e.name);return n?n.value:o&&o.value.ast?o.value.ast.value:void 0})(n);if(o&&(o=parseInt(o,10),o>0)){const o=t.convertNodeSourceSpanToLoc(n.startSourceSpan);e.report({messageId:"noPositiveTabindex",loc:o})}}})}});function h(){return(h=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e}).apply(this,arguments)}var b=a({name:"use-track-by-function",meta:{type:"suggestion",docs:{description:"Ensures trackBy function is used.",category:"Best Practices",recommended:!1},schema:[],messages:{useTrackByFunction:"Missing trackBy function in ngFor directive"}},defaultOptions:[],create(e){const t=i(e);return t.defineTemplateBodyVisitor({'BoundAttribute.inputs[name="ngForOf"]'(n){if(n.parent.inputs.some(e=>"BoundAttribute"===e.type&&"ngForTrackBy"===e.name))return;const o=t.convertNodeSourceSpanToLoc(n.sourceSpan);e.report({messageId:"useTrackByFunction",loc:o})},'BoundAttribute.templateAttrs[name="ngForOf"]'(n){const o=n.parent.templateAttrs;if(o.some(e=>"BoundAttribute"===e.type&&"ngForTrackBy"===e.name))return;const r=t.convertNodeSourceSpanToLoc(o[0].sourceSpan).start,s=t.convertNodeSourceSpanToLoc(o[o.length-1].sourceSpan).end,a={start:h({},r,{column:r.column-1}),end:h({},s,{column:s.column+1})};e.report({messageId:"useTrackByFunction",loc:a})}})}});module.exports={configs:{all:{extends:"./configs/base.json",rules:{"@angular-eslint/template/banana-in-box":"error","@angular-eslint/template/cyclomatic-complexity":"error","@angular-eslint/template/no-autofocus":"error","@angular-eslint/template/no-call-expression":"error","@angular-eslint/template/no-negated-async":"error","@angular-eslint/template/no-positive-tabindex":"error"}},base:{parser:"@angular-eslint/template-parser",plugins:["@angular-eslint/template"]},recommended:{extends:"./configs/base.json",rules:{"@angular-eslint/template/banana-in-box":"error","@angular-eslint/template/no-negated-async":"error"}},"process-inline-templates":{parser:"@typescript-eslint/parser",parserOptions:{ecmaVersion:2020,sourceType:"module"},plugins:["@angular-eslint/template"],processor:"@angular-eslint/template/extract-inline-html"}},processors:s,rules:{"banana-in-box":l,"cyclomatic-complexity":d,"no-autofocus":m,"no-call-expression":f,"no-negated-async":y,"no-positive-tabindex":x,"use-track-by-function":b}}; |
{ | ||
"name": "@angular-eslint/eslint-plugin-template", | ||
"version": "0.8.0-beta.4", | ||
"version": "0.8.0-beta.5", | ||
"description": "ESLint plugin for Angular Templates", | ||
@@ -29,3 +29,3 @@ "license": "MIT", | ||
"devDependencies": { | ||
"@angular-eslint/utils": "^0.8.0-beta.4" | ||
"@angular-eslint/utils": "^0.8.0-beta.5" | ||
}, | ||
@@ -37,3 +37,3 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "1fefc1803e636cbd76b952a7224f6ff53f25f024" | ||
"gitHead": "c8fa3d610d7438b430d5e90cd894dc090a92afb2" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23231
19
207
181