npm-package-json-lint
Version:
Configurable linter for package.json files.
36 lines (33 loc) • 18.4 kB
JavaScript
var Je=Object.create;var X=Object.defineProperty;var Le=Object.getOwnPropertyDescriptor;var $e=Object.getOwnPropertyNames;var Se=Object.getPrototypeOf,qe=Object.prototype.hasOwnProperty;var Ae=(n,e)=>()=>(e||n((e={exports:{}}).exports,e),e.exports);var Me=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of $e(e))!qe.call(n,o)&&o!==t&&X(n,o,{get:()=>e[o],enumerable:!(r=Le(e,o))||r.enumerable});return n};var g=(n,e,t)=>(t=n!=null?Je(Se(n)):{},Me(e||!n||!n.__esModule?X(t,"default",{value:n,enumerable:!0}):t,n));var Oe=Ae((Yn,rn)=>{rn.exports={name:"npm-package-json-lint",version:"0.0.0",description:"Configurable linter for package.json files.",keywords:["lint","linter","package.json","audit","auditor","npm-package-json-lint"],homepage:"https://github.com/tclindner/npm-package-json-lint",bugs:{url:"https://github.com/tclindner/npm-package-json-lint/issues"},author:"Thomas Lindner",repository:{type:"git",url:"https://github.com/tclindner/npm-package-json-lint.git"},bin:{npmPkgJsonLint:"dist/cli.js"},files:["CONTRIBUTING.md","dist"],main:"dist/api.js",types:"dist/src/api.d.ts",scripts:{build:"npm run esbuild && npm run tsc",esbuild:"node esbuild.config.js",eslint:"eslint .",npmpackagejsonlint:"node dist/cli.js ./package.json",lint:"npm run eslint && npm run npmpackagejsonlint",test:"npm run build && jest","test:ci":"jest --runInBand",tsc:"tsc --project tsconfig.json"},dependencies:{ajv:"^6.12.6","ajv-errors":"^1.0.1",chalk:"^4.1.2",cosmiconfig:"^8.3.6",debug:"^4.3.6",globby:"^11.1.0",ignore:"^5.3.2","is-plain-obj":"^3.0.0","jsonc-parser":"^3.3.1","log-symbols":"^4.1.0",meow:"^9.0.0",plur:"^4.0.0",semver:"^7.6.3",slash:"^3.0.0","strip-json-comments":"^3.1.1","type-fest":"^4.26.1","validate-npm-package-name":"^6.0.0"},devDependencies:{"@types/jest":"^30.0.0","@types/node":"^24.0.7","@typescript-eslint/eslint-plugin":"^8.35.0","@typescript-eslint/parser":"^8.35.0",esbuild:"^0.25.5","esbuild-node-externals":"^1.18.0",eslint:"^8.57.0","eslint-config-tc":"^27.0.0","eslint-config-typescript-tc":"^11.0.0","eslint-plugin-eslint-comments":"^3.2.0","eslint-plugin-import":"^2.32.0","eslint-plugin-jest":"^29.0.1","eslint-plugin-prettier":"^5.5.1","eslint-plugin-unicorn":"^56.0.1",figures:"^3.2.0",jest:"^30.0.3","npm-package-json-lint-config-default":"^8.0.0","npm-package-json-lint-config-tc":"^9.0.0",prettier:"^3.6.2","ts-jest":"^29.4.0",typescript:"^5.4.5"},engines:{node:">=20.0.0",npm:">=10.0.0"},license:"MIT"}});var Y=g(require("chalk")),Ee=g(require("meow"));var Fe=g(require("is-plain-obj")),z=g(require("slash"));var W=require("cosmiconfig");var Z=g(require("ajv")),ee=g(require("ajv-errors")),O=new Z.default({allErrors:!0,jsonPointers:!0});(0,ee.default)(O);var P=n=>n.map(e=>` - ${e.message}
`).join(""),M={type:"string",enum:["off","warning","error"],errorMessage:{type:"severity must be a string.",enum:'severity must be either "off", "warning", or "error".'}},Te=n=>({type:"array",items:[M,{type:"array",minItems:n,uniqueItems:!0,errorMessage:{type:"the second item in an array rule config must be an array.",minItems:"the second item in an array rule config must have at least 1 item.",uniqueItems:"the second item in an array rule config must have unique items."}}],minItems:2,maxItems:2,additionalItems:!1,errorMessage:{type:'rule config must be an array, e.g. ["error", ["value1", "value2"]].',minItems:'array rules must have two items, severity and options array. e.g. ["error", ["value1", "value2"]].',maxItems:'array rules must have two items, severity and options array. e.g. ["error", ["value1", "value2"]].',additionalItems:'array rules are only allowed two items, severity and the list is values. e.g. ["error", ["value1", "value2"]].'}}),Ve={type:"array",items:[M,{type:"object",errorMessage:{type:"the second item in an object rule config must be an object."}}],minItems:2,maxItems:2,additionalItems:!1,errorMessage:{type:'rule config must be an array, e.g. ["error", {}].',minItems:'object rules must have two items, severity and options object. e.g. ["error", {}].',maxItems:'object rules must have two items, severity and options object. e.g. ["error", {}].',additionalItems:'object rules are only allowed two items, severity and options object. e.g. ["error", {}].'}},Ne={type:"array",items:[{type:"string",errorMessage:{type:"each exception must be a string."}}],uniqueItems:!0,minItems:1,errorMessage:{type:"expections must be an array.",minItems:"expections must have at least 1 item.",uniqueItems:"expections must have unique items."}};var T=n=>{let e=O.compile(M);if(!e(n))throw new Error(`${P(e.errors)}`);return!0},ne=(n,e)=>{let t=O.compile(Te(e));if(!t(n))throw new Error(`${P(t.errors)}`);return!0},V=n=>{let e=O.compile(Ve);if(!e(n))throw new Error(`${P(e.errors)}`);return!0},te=n=>{let e=O.compile(Ne);if(!e(n))throw new Error(`${P(e.errors)}`);return!0};var We=n=>{if(typeof n=="string"&&n==="off")return!0;if(typeof n=="string"&&n!=="off")throw new Error(' - is an object type rule. It must be set to "off" if an object is not supplied.');return V(n)},He=n=>typeof n=="string"?T(n):V(n)&&n[1].hasOwnProperty("exceptions")?te(n[1].exceptions):!0,_e=(n,e)=>{if(typeof n=="string"&&n==="off")return!0;if(typeof n=="string"&&n!=="off")throw new Error(' - is an array type rule. It must be set to "off" if an array is not supplied.');return ne(n,e)},Be=n=>T(n),Ue=(n,e,t,r)=>{if(n)try{switch(n.ruleType){case"array":{_e(t,n.minItems);break}case"object":{We(t);break}case"optionalObject":{He(t);break}default:Be(t)}}catch(o){let s=`Configuration for rule "${e}" is invalid:
${o.message}`;throw typeof r=="string"?new Error(`${r}:
${s}`):new Error(s)}},re=(n,e,t)=>{n&&Object.keys(n).forEach(r=>{let o=t.get(r);Ue(o,r,n[r],e)})};var ue=g(require("path"));var R=g(require("path"));var oe=g(require("fs")),se=g(require("strip-json-comments")),Ge=n=>require(n),ze=n=>oe.default.readFileSync(n,"utf8").replace(/^\uFEFF/,""),ie=(n,e)=>{throw new Error(`Failed to read config file: ${n}.
Error: ${e.message}`)},Ye=Symbol("JSON source"),F=n=>{let e={},t="";try{t=ze(n),e=JSON.parse((0,se.default)(t))}catch(r){ie(n,r)}return Object.defineProperty(e,Ye,{value:t,enumerable:!1,writable:!1,configurable:!1}),e},ae=n=>{let e={};try{e=Ge(n)}catch(t){ie(n,t)}return e};var N=require("debug")("npm-package-json-lint:applyExtendsIfSpecified"),le=(n,e,t)=>{let r=n.extends;return Array.isArray(n.extends)||(r=[n.extends]),r.reduceRight((o,s)=>{try{let i=Ke(s,t),a={...i,...o},l={...i.rules,...o.rules},c=Array.isArray(i.plugins)?i.plugins:[],m=Array.isArray(o.plugins)?o.plugins:[],d=[...c,...m],C=[...new Set(d)],A=Array.isArray(i.overrides)?i.overrides:[],Ie=Array.isArray(o.overrides)?o.overrides:[],Q=[...A,...Ie];return a.rules=l,d.length>0&&(a.plugins=C),Q.length>0&&(a.overrides=Q),a}catch(i){throw i.message+=`
Referenced from: ${e}`,i}},n)},Ke=(n,e)=>{let t={},r=n;if(n.startsWith("./"))r=R.default.join(process.cwd(),n),t=Qe(r);else{let o=require.resolve(r,{paths:[R.default.dirname(e)]});t=require(o)}return Object.keys(t).length>0&&t.extends&&(t=le(t,r,e)),t},Qe=n=>{let e={};switch(R.default.extname(n)){case".js":{e=ae(n);break}case".json":{e=F(n);break}default:throw new Error(`Unsupport config file extension. File path: ${n}`)}return e},E=(n,e)=>{let t={...n};return N("Loading extends, if applicable"),t!=null&&t.hasOwnProperty("extends")&&t.extends&&(N("extends property present, applying."),t=le(t,e,e)),N("Loading extends complete"),t};var ce=g(require("path")),ge=g(require("globby")),j=require("debug")("npm-package-json-lint:applyOverrides"),I=(n,e,t,r)=>{let o={...t};return j("overrides"),j(r),r&&r.forEach(s=>{let a=s.patterns.filter(c=>c.length).map(c=>c.endsWith("/package.json")?c:`${c}/package.json`),l=ge.default.sync(a,{cwd:n,gitignore:!0});j("globFiles"),j(l),l.forEach(c=>{let m=ce.default.resolve(n,c);e===m&&(o={...o,...s.rules})})}),j("finalRules"),j(o),o};var y=require("debug")("npm-package-json-lint:cosmicConfigTransformer"),D=(n,e,t)=>(y(`cwd: ${n}`),y("configBaseDirectory"),y(e),r=>{if(y("cosmiconfigResult"),y(r),!r)return null;let{config:o,filepath:s}=r;y("cosmiconfigResult.config"),y(o),y("cosmiconfigResult.filepath"),y(s);let i=e||ue.default.dirname(s||""),a={...o},l=E(a,t);return I(n,t,l.rules,l.overrides)});var p=require("debug")("npm-package-json-lint:Config"),Xe=0,J=class{constructor(e,t,r,o,s){t&&(this.config=E(t,"PassedConfig")),this.cwd=e,this.configFile=r,this.configBaseDirectory=o,this.rules=s}getConfigForFile(e){p(`Getting config for ${e}`);let t=e;p(`filePathToSearch: ${t}`);let r;if(typeof this.config>"u")p("User passed config is undefined."),this.configFile?(p("Config file specified, loading it."),r=(0,W.cosmiconfigSync)("npmpackagejsonlint",{transform:D(this.cwd,this.configBaseDirectory,e)}).load(this.configFile)):(p("Config file wasn't specified, searching for config."),r=(0,W.cosmiconfigSync)("npmpackagejsonlint",{transform:D(this.cwd,this.configBaseDirectory,t)}).search(t));else{p("User passed config is set, using it.");let o=this.config;p(`Applying overrides to config for ${e}`),r=I(this.cwd,e,o.rules,o.overrides),p(`Overrides applied for ${e}`)}if(!r)throw new Error(`No npm-package-json-lint configuration found.
${t}`);if(Object.keys(r).length===Xe)throw new Error(`No rules specified in configuration.
${t}`);return p(`Overrides applied for ${e}`),p("Final Config"),p(r),re(r,"cli",this.rules),r}};var pe=g(require("chalk")),me=require("fs"),H=g(require("path")),L=class{constructor(){this.rules={}}load(){let e=H.default.join(__dirname,"rules");try{return(0,me.readdirSync)(e).forEach(t=>{let s=t.slice(0,-3),i=H.default.join(e,t);this.registerRule(s,i)}),this.rules}catch(t){throw new Error(`Error while loading rules from rules directory - ${t.message}`)}}get(e){if(typeof this.rules[e]>"u"){let o=`Rule, ${e}, is invalid. Please ensure it matches a valid option.`;throw new Error(pe.default.bold.red(o))}return require(this.rules[e])}getRules(){return this.rules}registerRule(e,t){this.rules[e]=t}};var w=g(require("path"));var de=n=>n.reduce((t,r)=>{let o=r.severity==="error",s=o?t.errorCount+1:t.errorCount,i=o?t.warningCount:t.warningCount+1;return{errorCount:s,warningCount:i}},{errorCount:0,warningCount:0}),_=n=>n.reduce((e,t)=>({ignoreCount:t.ignored?e.ignoreCount+1:e.ignoreCount,errorCount:e.errorCount+t.errorCount,warningCount:e.warningCount+t.warningCount}),{ignoreCount:0,errorCount:0,warningCount:0});var u=require("debug")("npm-package-json-lint:linter"),B=n=>{let{cwd:e,fileName:t,ignored:r,issues:o,errorCount:s,warningCount:i}=n;return{filePath:`./${w.default.relative(e,t)}`,issues:o,ignored:r,errorCount:s,warningCount:i}},Ze=(n,e,t)=>{let r=[];for(let o in e){let s=t.get(o),i="off",a;if(s.ruleType==="array"||s.ruleType==="object"?(i=typeof e[o]=="string"&&e[o]==="off"?e[o]:e[o][0],a=typeof e[o]=="string"?{}:e[o][1]):s.ruleType==="optionalObject"?typeof e[o]=="string"?(i=e[o],a={}):(i=e[o][0],a=e[o][1]):i=e[o],i!=="off"){let l=s.lint(n,i,a);l!==null&&r.push(l)}}return r},ye=(n,e,t,r,o)=>{let s=Ze(e,t,o),i=de(s);return B({cwd:n,fileName:r,ignored:!1,issues:s,errorCount:i.errorCount,warningCount:i.warningCount})},en=(n,e,t,r)=>{let o=F(w.default.resolve(e));return ye(n,o,t,e,r)},he=n=>{let{cwd:e,packageJsonObject:t,filename:r,ignorer:o,configHelper:s,rules:i}=n;u("executing on package.json object");let a=[],l=r||"",c=w.default.isAbsolute(l)?l:w.default.resolve(e,l),m=w.default.relative(e,c);if(o.ignores(m)){u(`Ignored: ${m}`);let C=B({cwd:e,fileName:c,ignored:!0,issues:[],errorCount:0,warningCount:0});a.push(C)}else{u(`Getting config for ${c}`);let C=s.getConfigForFile(c);u(`Config fetched for ${c}`);let A=ye(e,t,C,c,i);a.push(A)}u("Aggregating overall counts");let d=_(a);return u("stats"),u(d),{results:a,ignoreCount:d.ignoreCount,errorCount:d.errorCount,warningCount:d.warningCount}},be=n=>{let{cwd:e,fileList:t,ignorer:r,configHelper:o,rules:s}=n;u("executing on package.json files");let i=t.map(l=>{let c=w.default.relative(e,l);if(r.ignores(c))return u(`Ignored: ${c}`),B({cwd:e,fileName:l,ignored:!0,issues:[],errorCount:0,warningCount:0});u(`Getting config for ${l}`);let m=o.getConfigForFile(l);return u(`Config fetched for ${l}`),en(e,l,m,s)});u("Aggregating overall counts");let a=_(i);return u("stats"),u(a),{results:i,ignoreCount:a.ignoreCount,errorCount:a.errorCount,warningCount:a.warningCount}};var we=g(require("path")),je=g(require("globby")),f=require("debug")("npm-package-json-lint:getFileList"),ke=(n,e)=>{f("patterns"),f(n);let t=n.filter(a=>a.length);f("filteredPatterns"),f(t);let r=t.map(a=>a.endsWith("/package.json")?a:`${a}/**/package.json`);r.push("!**/node_modules/**"),f("globPatterns"),f(r);let o=[],s=new Set,i=je.default.sync(r,{cwd:e,gitignore:!0});return f("globFiles"),f(i),i.forEach(a=>{let l=we.default.resolve(e,a);s.has(l)||(s.add(l),o.push(l))}),f("Final file list from `getFileList`"),f(o),o};var xe=g(require("fs")),G=g(require("path")),ve=g(require("ignore")),U=require("debug")("npm-package-json-lint:getIgnorer"),nn=".npmpackagejsonlintignore",tn="ENOENT",Ce=(n,e)=>{let t=e||nn;U(`ignoreFilePath: ${t}`);let r=G.default.isAbsolute(t)?t:G.default.resolve(n,t);U(`absoluteIgnoreFilePath: ${r}`);let o="";try{o=xe.default.readFileSync(r,"utf8")}catch(s){if(s.code!==tn)throw s}return U("Ignore text added"),(0,ve.default)().add(o)};var $=require("debug")("npm-package-json-lint:NpmPackageJsonLint"),on=Oe(),sn=0,an=n=>n.severity==="error",Pe=n=>(0,Fe.default)(n),ln=(n,e)=>!e&&!Pe(n)||e&&(n||Pe(n)),cn=n=>{let e=[];return n.forEach(t=>{let r=t.issues.filter(an);if(r.length>sn){let o={issues:r,errorCount:r.length,warningCount:0};e.push(Object.assign(t,o))}}),e},S=class{constructor(e){let{cwd:t,packageJsonObject:r,packageJsonFilePath:o,config:s,configFile:i,configBaseDirectory:a,patterns:l,quiet:c,ignorePath:m,fix:d}=e;this.cwd=(0,z.default)(t||process.cwd()),this.packageJsonObject=r,this.packageJsonFilePath=o&&(0,z.default)(o),this.patterns=l,this.quiet=c||!1,this.ignorePath=m||"",this.fix=d||!1,this.version=on.version,this.rules=new L,this.rules.load(),this.configHelper=new J(this.cwd,s,i,a,this.rules)}lint(){if($("Starting lint"),ln(this.packageJsonObject,this.patterns))throw new Error("You must pass npm-package-json-lint a `patterns` glob or a `packageJsonObject` string, though not both.");let e=Ce(this.cwd,this.ignorePath),t;if(this.patterns){if($("Linting using patterns"),!Array.isArray(this.patterns))throw new TypeError("Patterns must be an array.");let r=ke(this.patterns,this.cwd);t=be({cwd:this.cwd,fileList:r,ignorer:e,configHelper:this.configHelper,rules:this.rules})}else $("Linting using passed object."),t=he({cwd:this.cwd,packageJsonObject:this.packageJsonObject,ignorer:e,filename:this.packageJsonFilePath,configHelper:this.configHelper,rules:this.rules});return this.quiet?{results:cn(t.results),ignoreCount:t.ignoreCount,errorCount:t.errorCount,warningCount:t.warningCount}:($("lint complete"),t)}};var h=g(require("chalk")),k=g(require("plur")),q=0,gn=1,un=n=>{n.forEach(e=>{console.log(e.toString())})},pn=(n,e)=>{let{filePath:t,issues:r,ignored:o,errorCount:s,warningCount:i}=n;if(o)console.log(""),console.log(`${h.default.yellow.underline(t)} - ignored`);else if(s>q||!e&&i>q){console.log(""),console.log(h.default.underline(t)),un(r);let a=`${s} ${(0,k.default)("error",s)}`,l=`${i} ${(0,k.default)("warning",i)}`;console.log(h.default.red.bold(a)),e||console.log(h.default.yellow.bold(l))}},mn=(n,e)=>{let{errorCount:t,warningCount:r,ignoreCount:o}=n;if(t>q||r>q){let s=`${t} ${(0,k.default)("error",t)}`,i=`${r} ${(0,k.default)("warning",r)}`,a=`${o} ${(0,k.default)("file",o)} ignored`;console.log(""),console.log(h.default.underline("Totals")),console.log(h.default.red.bold(s)),e||(console.log(h.default.yellow.bold(i)),console.log(h.default.yellow.bold(a)))}},Re=(n,e)=>{n.results.forEach(t=>{pn(t,e)}),n.results.length>gn&&mn(n,e)};var b=require("debug")("npm-package-json-lint:cli"),v={zeroClean:0,oneMissingTarget:1,twoLintErrorsDetected:2,runTimeException:3,exceedMaxWarnings:4},fn=(0,Ee.default)(`
Usage
$ npmPkgJsonLint <patterns>
Options
--quiet, -q Report errors only
--noConfigFiles, -ncf Disables use of .npmpackagejsonlintrc.json files, npmpackagejsonlint.config.js files, and npmpackagejsonlint object in package.json file.
--configFile, -c File path of .npmpackagejsonlintrc.json
--ignorePath, -i Path to a file containing patterns that describe files to ignore. The path can be absolute or relative to process.cwd(). By default, npm-package-json-lint looks for .npmpackagejsonlintignore in process.cwd().
--maxWarnings, -mw Maximum number of warnings that can be detected before an error is thrown.
--allowEmptyTargets Do not throw an error when a list of targets is empty.
Examples
$ npmPkgJsonLint --version
$ npmPkgJsonLint .
$ npmPkgJsonLint ./packages
$ npmPkgJsonLint ./package1 ./package2
$ npmPkgJsonLint -c ./config/.npmpackagejsonlintrc.json .
$ npmPkgJsonLint --configFile ./config/npmpackagejsonlint.config.json .
$ npmPkgJsonLint -q .
$ npmPkgJsonLint --quiet ./packages
$ npmPkgJsonLint . --ignorePath .gitignore
$ npmPkgJsonLint . -i .gitignore
$ npmPkgJsonLint . --maxWarnings 10
$ npmPkgJsonLint . -mw 10
`,{flags:{quiet:{type:"boolean",alias:"q",default:!1},noConfigFiles:{type:"boolean",alias:"ncf",default:!1},configFile:{type:"string",alias:"c",default:""},ignorePath:{type:"string",alias:"i",default:""},maxWarnings:{type:"number",alias:"mw",default:1e7},allowEmptyTargets:{type:"boolean",default:!1}}}),{input:dn,flags:x}=fn,yn=0,K=dn;b(`patterns: ${K}`);if(K.length===yn){b("No lint targets provided"),console.log(Y.default.red.bold("No lint targets provided"));let n=x.allowEmptyTargets?v.zeroClean:v.oneMissingTarget;process.exit(n)}try{let n=v.zeroClean,e=0;b("Creating NpmPackageJsonLint instance");let r=new S({cwd:process.cwd(),configFile:x.configFile,patterns:K,ignorePath:x.ignorePath,quiet:x.quiet}).lint();b("NpmPackageJsonLint.lint complete"),b("Reporter.write starting"),Re(r,x.quiet),b("Reporter.write complete"),r.warningCount>x.maxWarnings&&(b("Max warnings exceeded"),n=v.exceedMaxWarnings),r.errorCount>e&&(b("Lint errors detected"),n=v.twoLintErrorsDetected),process.exit(n)}catch(n){console.log(Y.default.red.bold(n.message)),process.exit(v.runTimeException)}
//# sourceMappingURL=cli.js.map