@fabriziosalmi/slopless 1.9.0 → 1.11.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/dist/engine/api.js +1 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +140 -139
- package/package.json +1 -1
- package/rules/VBC-001.yaml +14 -1
package/dist/engine/api.js
CHANGED
|
@@ -517,7 +517,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
517
517
|
`).length-(u.endsWith(`
|
|
518
518
|
`)?1:0),k=ine(d,u);if(k.length===0)return x;let m=k.reduce((T,F)=>T+u.slice(F.start,F.end).split(`
|
|
519
519
|
`).length-1,0);return x-m}var hCe=new Set(["empty-file","file-length-limit"]),cne=class{static check(d,x,k){var X,be;let m=[],T=k!==void 0?k:JHe.readFileSync(d,"utf8");for(let Pe of x){let Be=(X=Pe.match.ast_check)==null?void 0:X.type;!Be||!hCe.has(Be)||WE(d,Pe)||Pe.match.file_types&&!Pe.match.file_types.includes(KF(d))||m.push(...this.checkByCounting(Be,T,{rule:Pe,file:d,threshold:Pe.match.threshold??((be=Pe.match.ast_check)==null?void 0:be.threshold)}))}if(!sne.has(KF(d)))return m;let F=va.createSourceFile(d,T,va.ScriptTarget.Latest,!0);for(let Pe of x){if(!Pe.match.ast_check||WE(d,Pe))continue;let Be=Pe.match.ast_check.type,at=Pe.match.threshold??Pe.match.ast_check.threshold,pe={rule:Pe,sourceFile:F,file:d,violations:m};hCe.has(Be)||this.traverse(F,Zt=>{Be==="function-params-limit"&&at&&this.checkFunctionParams(Zt,at,pe),Be==="function-length-limit"&&at&&this.checkFunctionLength(Zt,at,pe),Be==="empty-catch"&&this.checkEmptyCatch(Zt,pe),Be==="nested-blocks-limit"&&at&&this.checkNestedBlocks(Zt,at,pe),Be==="empty-block"&&this.checkEmptyBlock(Zt,pe),Be==="one-liner-limit"&&this.checkOneLiner(Zt,pe),Be==="redundant-if-true"&&this.checkRedundantIfTrue(Zt,pe),Be==="lying-function-names"&&this.checkLyingFunctionName(Zt,pe),Be==="empty-interface"&&this.checkEmptyInterface(Zt,pe),Be==="async-without-await"&&this.checkAsyncWithoutAwait(Zt,pe)})}return m}static checkFunctionParams(d,x,k){if(!va.isFunctionDeclaration(d)&&!va.isMethodDeclaration(d)&&!va.isArrowFunction(d)||d.parameters.length<=x)return;let{line:m}=k.sourceFile.getLineAndCharacterOfPosition(d.getStart());k.violations.push({ruleId:k.rule.id,name:k.rule.name,severity:k.rule.severity,message:this.formatMessage(k.rule.message,{threshold:x,count:d.parameters.length,line:m+1}),file:k.file,line:m+1})}static checkFunctionLength(d,x,k){if(!va.isFunctionDeclaration(d)&&!va.isMethodDeclaration(d)&&!va.isArrowFunction(d))return;let m=k.sourceFile.getLineAndCharacterOfPosition(d.getStart()).line,F=k.sourceFile.getLineAndCharacterOfPosition(d.getEnd()).line-m+1;F<=x||k.violations.push({ruleId:k.rule.id,name:k.rule.name,severity:k.rule.severity,message:this.formatMessage(k.rule.message,{threshold:x,count:F,line:m+1}),file:k.file,line:m+1})}static checkEmptyCatch(d,x){if(!va.isCatchClause(d)||d.block.statements.length>0||/\/\/|\/\*/.test(d.block.getText()))return;let{line:k}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:k+1}),file:x.file,line:k+1})}static checkEmptyBlock(d,x){if(!va.isBlock(d)||d.statements.length>0||va.isCatchClause(d.parent)||va.isConstructorDeclaration(d.parent))return;let{line:k}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:k+1}),file:x.file,line:k+1})}static checkOneLiner(d,x){if(!va.isBlock(d))return;let k=new Map;if(d.statements.forEach(F=>{let{line:X}=x.sourceFile.getLineAndCharacterOfPosition(F.getStart());k.set(X,(k.get(X)??0)+1)}),d.statements.length<=k.size)return;let T=[...k.entries()].filter(([,F])=>F>1).map(([F])=>F).sort((F,X)=>F-X)[0]??x.sourceFile.getLineAndCharacterOfPosition(d.getStart()).line;x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:T+1}),file:x.file,line:T+1})}static checkRedundantIfTrue(d,x){if(!va.isIfStatement(d))return;let k=d.expression;if(!(k.kind===va.SyntaxKind.TrueKeyword||va.isBinaryExpression(k)&&k.left.kind===va.SyntaxKind.TrueKeyword&&k.operatorToken.kind===va.SyntaxKind.EqualsEqualsEqualsToken))return;let{line:T}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:T+1}),file:x.file,line:T+1})}static checkLyingFunctionName(d,x){var Pe,Be;if(!va.isFunctionDeclaration(d)&&!va.isMethodDeclaration(d))return;let k=(Pe=d.name)==null?void 0:Pe.getText();if(!k||!new RegExp("^(get|is|has|retrieve)","i").test(k))return;let T=(Be=d.body)==null?void 0:Be.getText();if(!T)return;let F=/\bthis\.[\w.]*\.(?:set|delete|remove|update|write|modify|pop|push|shift|unshift|splice|clear|add|sort|reverse)\s*\(/,X=/\bObject\.assign\s*\(\s*this\b|\bdelete\s+this\./;if(!F.test(T)&&!O3t(d.body)&&!X.test(T))return;let{line:be}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{name:k,line:be+1}),file:x.file,line:be+1})}static checkByCounting(d,x,k){let{rule:m,file:T,threshold:F}=k;if(d==="empty-file"&&x.trim().length===0)return[{ruleId:m.id,name:m.name,severity:m.severity,message:this.formatMessage(m.message,{file:T,line:0}),file:T,line:0}];if(d==="file-length-limit"&&F){let X=R3t(x,KF(T));if(X>F)return[{ruleId:m.id,name:m.name,severity:m.severity,message:this.formatMessage(m.message,{threshold:F,count:X}),file:T,line:0}]}return[]}static checkEmptyInterface(d,x){if(!va.isInterfaceDeclaration(d)||d.members.length>0||d.heritageClauses&&d.heritageClauses.length>0)return;let{line:k}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:k+1}),file:x.file,line:k+1})}static checkAsyncWithoutAwait(d,x){let k=UHe(d);if(!k)return;let m=va.canHaveModifiers(d)?va.getModifiers(d):void 0;if(!((m==null?void 0:m.some(X=>X.kind===va.SyntaxKind.AsyncKeyword))??!1)||va.isCallExpression(d.parent)&&d.parent.arguments.includes(d)||M3t(k))return;let{line:F}=x.sourceFile.getLineAndCharacterOfPosition(d.getStart());x.violations.push({ruleId:x.rule.id,name:x.rule.name,severity:x.rule.severity,message:this.formatMessage(x.rule.message,{line:F+1}),file:x.file,line:F+1})}static checkNestedBlocks(d,x,k){if(!one(d))return;let m=0,T=(X,be)=>{m=Math.max(m,be),va.forEachChild(X,Pe=>{if(one(Pe)){T(Pe,0);return}let at=!(va.isIfStatement(Pe)&&va.isIfStatement(X)&&X.elseStatement===Pe)&&(va.isIfStatement(Pe)||va.isForStatement(Pe)||va.isForOfStatement(Pe)||va.isForInStatement(Pe)||va.isWhileStatement(Pe)||va.isDoStatement(Pe)||va.isSwitchStatement(Pe)||va.isTryStatement(Pe));T(Pe,at?be+1:be)})};if(T(d,0),m<=x)return;let{line:F}=k.sourceFile.getLineAndCharacterOfPosition(d.getStart());k.violations.push({ruleId:k.rule.id,name:k.rule.name,severity:k.rule.severity,message:this.formatMessage(k.rule.message,{threshold:x,count:m,line:F+1}),file:k.file,line:F+1})}static traverse(d,x){x(d),va.forEachChild(d,k=>this.traverse(k,x))}static formatMessage(d,x){let k=d;for(let[m,T]of Object.entries(x))k=k.replace(new RegExp(`\\{${m}\\}`,"g"),String(T));return k}};var VHe=DS(require("fs"));var lne=class{static async check(d,x,k){let m=[],T=k!==void 0?k:VHe.readFileSync(d,"utf8");for(let F of x)WE(d,F)||(F.match.heuristic_check==="link-checker"&&d.endsWith(".md")&&m.push(...await this.findBrokenLinks(d,T,F)),F.match.heuristic_check==="stale-copyright-year"&&m.push(...this.findStaleCopyright(d,T,F)));return m}static findStaleCopyright(d,x,k){let m=[],T=new Date().getFullYear(),F=/(?:©|\(c\)|copyright)\s*((?:19|20)\d{2})(?:\s*[-–—]\s*((?:19|20)?\d{2}|present))?/gi;return x.split(`
|
|
520
|
-
`).forEach((X,be)=>{for(let Pe of X.matchAll(F)){let[,Be,at]=Pe;at&&/present/i.test(at)||Number(at?at.length===2?Be.slice(0,2)+at:at:Be)>=T||m.push({ruleId:k.id,name:k.name,severity:k.severity,message:k.message.replace("{line}",String(be+1)).replace("{match}",Pe[0]).replace("{year}",String(T)),file:d,line:be+1})}}),m}static async findBrokenLinks(d,x,k){let m=[];for(let T of this.extractLinks(x)){if(await this.checkLink(T))continue;let F=this.getLineNumber(x,T);m.push({ruleId:k.id,name:k.name,severity:k.severity,message:this.formatMessage(k.message,{url:T,match:T,line:F}),file:d,line:F})}return m}static extractLinks(d){let x=/\[.*?\]\((https?:\/\/.*?)\)/g,k=[],m;for(;(m=x.exec(d))!==null;)k.push(m[1]);return k}static async checkLink(d){try{
|
|
520
|
+
`).forEach((X,be)=>{for(let Pe of X.matchAll(F)){let[,Be,at]=Pe;at&&/present/i.test(at)||Number(at?at.length===2?Be.slice(0,2)+at:at:Be)>=T||m.push({ruleId:k.id,name:k.name,severity:k.severity,message:k.message.replace("{line}",String(be+1)).replace("{match}",Pe[0]).replace("{year}",String(T)),file:d,line:be+1})}}),m}static async findBrokenLinks(d,x,k){let m=[];for(let T of this.extractLinks(x)){if(await this.checkLink(T)!=="broken")continue;let F=this.getLineNumber(x,T);m.push({ruleId:k.id,name:k.name,severity:k.severity,message:this.formatMessage(k.message,{url:T,match:T,line:F}),file:d,line:F})}return m}static extractLinks(d){let x=/\[.*?\]\((https?:\/\/.*?)\)/g,k=[],m;for(;(m=x.exec(d))!==null;)k.push(m[1]);return k}static async checkLink(d){for(let x of["HEAD","GET"])try{let k=await fetch(d,{method:x,signal:AbortSignal.timeout(5e3)});if(k.ok)return"ok";if(x==="GET")return k.status===404||k.status===410?"broken":"unknown"}catch(k){if(this.isUnknownHost(k))return"broken"}return"unknown"}static isUnknownHost(d){var k;let x=(k=d==null?void 0:d.cause)==null?void 0:k.code;return x==="ENOTFOUND"||x==="EAI_AGAIN"}static getLineNumber(d,x){let k=d.indexOf(x);return k===-1?0:d.substring(0,k).split(`
|
|
521
521
|
`).length}static formatMessage(d,x){let k=d;for(let[m,T]of Object.entries(x))k=k.replace(new RegExp(`\\{${m}\\}`,"g"),String(T));return k}};var hp=DS(tne()),WHe=DS(require("fs"));var une=class{static check(d,x,k){let m=[];if(!sne.has(KF(d)))return[];let T=k!==void 0?k:WHe.readFileSync(d,"utf8"),F=hp.createSourceFile(d,T,hp.ScriptTarget.Latest,!0);for(let X of x){if(!X.match.semantic_check||WE(d,X))continue;let be=X.match.semantic_check;this.traverse(F,Pe=>{if(be==="boolean-naming"&&hp.isVariableDeclaration(Pe)&&Pe.initializer){let Be=Pe.name.getText();if(this.isBooleanType(Pe)&&!new RegExp("^(is|has|can|should|was|did|do|will)","i").test(Be)){let{line:pe}=F.getLineAndCharacterOfPosition(Pe.getStart());m.push({ruleId:X.id,name:X.name,severity:X.severity,message:this.formatMessage(X.message,{name:Be,line:pe+1}),file:d,line:pe+1})}}if(be==="boolean-redundancy"&&hp.isIfStatement(Pe)){let Be=Pe.thenStatement,at=Pe.elseStatement;if(Be&&at&&this.isReturnBoolean(Be)&&this.isReturnBoolean(at)){let{line:pe}=F.getLineAndCharacterOfPosition(Pe.getStart());m.push({ruleId:X.id,name:X.name,severity:X.severity,message:this.formatMessage(X.message,{line:pe+1}),file:d,line:pe+1})}}if(be==="collection-suffix"&&hp.isVariableDeclaration(Pe)&&Pe.initializer){let Be=Pe.name.getText(),at=this.isArrayType(Pe),pe=!Be.endsWith("s")&&!new RegExp("(List|Map|Set|Collection|Array)$","i").test(Be);if(at&&pe){let{line:Zt}=F.getLineAndCharacterOfPosition(Pe.getStart());m.push({ruleId:X.id,name:X.name,severity:X.severity,message:this.formatMessage(X.message,{name:Be,line:Zt+1}),file:d,line:Zt+1})}}if(be==="semantic-shadowing"&&hp.isVariableDeclaration(Pe)){let Be=Pe.name.getText();if(["fs","path","crypto","http","https","express","os","child_process","cluster","dns"].includes(Be)){let{line:pe}=F.getLineAndCharacterOfPosition(Pe.getStart());m.push({ruleId:X.id,name:X.name,severity:X.severity,message:this.formatMessage(X.message,{name:Be,line:pe+1,match:Be}),file:d,line:pe+1})}}})}return m}static traverse(d,x){x(d),hp.forEachChild(d,k=>this.traverse(k,x))}static formatMessage(d,x){let k=d;for(let[m,T]of Object.entries(x))k=k.replace(new RegExp(`\\{${m}\\}`,"g"),String(T));return k}static isBooleanType(d){var x,k,m;return((x=d.type)==null?void 0:x.kind)===hp.SyntaxKind.BooleanKeyword||((k=d.initializer)==null?void 0:k.kind)===hp.SyntaxKind.TrueKeyword||((m=d.initializer)==null?void 0:m.kind)===hp.SyntaxKind.FalseKeyword}static isArrayType(d){var x,k;return((x=d.type)==null?void 0:x.kind)===hp.SyntaxKind.ArrayType||((k=d.initializer)==null?void 0:k.kind)===hp.SyntaxKind.ArrayLiteralExpression}static isReturnBoolean(d){let x=d;return hp.isBlock(x)&&x.statements.length===1&&(x=x.statements[0]),hp.isReturnStatement(x)&&x.expression?x.expression.kind===hp.SyntaxKind.TrueKeyword||x.expression.kind===hp.SyntaxKind.FalseKeyword:!1}};function qHe(u,d){var m;let x=new Map;for(let T of d)(m=T.supersedes)!=null&&m.length&&x.set(T.id,T.supersedes);if(x.size===0)return u;let k=new Map;for(let T of u){let F=x.get(T.ruleId);if(!F)continue;let X=`${T.file}:${T.line}`,be=k.get(X);be||(be=new Set,k.set(X,be));for(let Pe of F)be.add(Pe)}return k.size===0?u:u.filter(T=>{var F;return!((F=k.get(`${T.file}:${T.line}`))!=null&&F.has(T.ruleId))})}var _ne=DS(require("path")),j3t=_ne.join(__dirname,"..","..","rules");async function $3t(u,d,x){let k=LUe(x),m=[j3t];if(k.customRulesPaths)for(let be of k.customRulesPaths)m.push(_ne.resolve(process.cwd(),be));let T=Xte.loadRules(m);k.rules&&(T=T.map(be=>{let Pe=k.rules[be.id];return Pe?{...be,severity:Pe}:be}).filter(be=>be.severity!=="off"));let F=new Set(Object.keys(k.rules??{}));T=T.filter(be=>!be.opt_in||F.has(be.id));let X=[];return X=X.concat(ane.check(d,T,u)),X=X.concat(cne.check(d,T,u)),X=X.concat(une.check(d,T,u)),X=X.concat(await lne.check(d,T,u)),qHe(X,T)}0&&(module.exports={lintText});
|
|
522
522
|
/*! Bundled license information:
|
|
523
523
|
|
package/dist/index.d.ts
CHANGED
|
@@ -122,7 +122,22 @@ type Rule = z.infer<typeof RuleSchema>;
|
|
|
122
122
|
* Paths outside the project cannot be matched by a .sloplessignore rule at all,
|
|
123
123
|
* so they pass through untouched.
|
|
124
124
|
*/
|
|
125
|
+
/**
|
|
126
|
+
* Paths that are never the author's code. Without these a Django project's
|
|
127
|
+
* collected staticfiles reported 1,122 findings inside xregexp.js and 319 inside
|
|
128
|
+
* jquery.js — real matches, in libraries nobody in that repository wrote.
|
|
129
|
+
*
|
|
130
|
+
* `--init` has written these into `.sloplessignore` all along; applying them
|
|
131
|
+
* without being asked is the difference between a tool that is right by default
|
|
132
|
+
* and one that is right once you have read the manual.
|
|
133
|
+
*/
|
|
134
|
+
declare const NEVER_YOURS: string[];
|
|
125
135
|
declare function applyIgnoreRules(files: string[], configIgnore?: string[]): string[];
|
|
136
|
+
/** The files that survive the ignore rules, and how many each source removed. */
|
|
137
|
+
declare function partitionIgnored(files: string[], configIgnore?: string[]): {
|
|
138
|
+
kept: string[];
|
|
139
|
+
vendored: number;
|
|
140
|
+
};
|
|
126
141
|
/**
|
|
127
142
|
* Narrows the rule set before anything runs.
|
|
128
143
|
*
|
|
@@ -138,4 +153,4 @@ declare function selectRules(rules: Rule[], options: {
|
|
|
138
153
|
minSeverity?: string;
|
|
139
154
|
}): Rule[];
|
|
140
155
|
|
|
141
|
-
export { UsageError, applyIgnoreRules, selectRules };
|
|
156
|
+
export { NEVER_YOURS, UsageError, applyIgnoreRules, partitionIgnored, selectRules };
|