@funish/basis
Version:
A unified development toolkit with CLI for package management, versioning, publishing, linting, and git hooks management for JavaScript/TypeScript projects.
4 lines (3 loc) • 10.1 kB
JavaScript
;const node_child_process=require("node:child_process"),promises=require("node:fs/promises"),consola=require("consola"),P=require("fast-glob"),x=require("micromatch"),nypm=require("nypm"),pathe=require("pathe"),pkgTypes=require("pkg-types"),utils=require("./basis.D3fInv-P.cjs");function _interopDefaultCompat(t){return t&&typeof t=="object"&&"default"in t?t.default:t}const P__default=_interopDefaultCompat(P),x__default=_interopDefaultCompat(x);function getStagedFiles(){try{const t=node_child_process.execSync("git diff --cached --name-only",{encoding:"utf8"}).trim().split(`
`).filter(Boolean),i=node_child_process.execSync("git diff --cached --name-only --diff-filter=D",{encoding:"utf8"}),o=new Set(i.trim().split(`
`).filter(Boolean));return t.filter(s=>!o.has(s))}catch{return[]}}async function getProjectFiles(t,i=["**/*"],o=["node_modules/**","dist/**","build/**",".git/**"]){try{return await P__default(i,{cwd:t,ignore:o,onlyFiles:!0,dot:!1,absolute:!1})}catch(s){return consola.consola.warn("Failed to scan project files:",s),[]}}async function lintStaged(t=process.cwd(),i){const{config:o}=await utils.loadConfig({cwd:t,overrides:i?{lint:{staged:i}}:void 0}),s=o.lint?.staged||{},e=getStagedFiles();if(e.length===0)return!0;if(Object.keys(s).length===0)return consola.consola.warn("No staged lint configuration found. Add lint.staged section to your basis.config.ts"),!0;let r=!1;const n=new Set;for(const[a,f]of Object.entries(s)){const u=e.filter(l=>!n.has(l)&&x__default.isMatch(l.split("/").pop()||l,a));if(u.length!==0){consola.consola.start(`Linting ${u.length} files: ${a}`);try{const l=`${f} ${u.join(" ")}`;node_child_process.execSync(l,{stdio:"inherit",cwd:t});const d=[];for(const c of u)await utils.fileExists(pathe.resolve(t,c))&&d.push(c);d.length>0&&node_child_process.execSync(`git add ${d.join(" ")}`,{stdio:"inherit",cwd:t}),u.forEach(c=>n.add(c))}catch(l){r=!0,consola.consola.error(`Lint pattern '${a}' failed. Please fix the issues and try again:`,l)}}}return!r}async function lintProject(t=process.cwd(),i){const{config:o}=await utils.loadConfig({cwd:t,overrides:i?{lint:{project:i}}:void 0}),s=o.lint?.project||{};if(Object.keys(s).length===0)return consola.consola.warn("No project lint configuration found. Add lint.project section to your basis.config.ts"),!0;consola.consola.start("Running project-wide linting...");let e=!1;for(const[r,n]of Object.entries(s)){consola.consola.start(`Running project lint: ${r}`);try{node_child_process.execSync(n,{stdio:"inherit",cwd:t})}catch(a){e=!0,consola.consola.error(`Project lint pattern '${r}' failed. Please fix the issues and try again:`,a)}}return!e}async function lintDependencies(t=process.cwd(),i,o=!1){const{config:s}=await utils.loadConfig({cwd:t,overrides:i?{lint:{dependencies:i}}:void 0}),e=s.lint?.dependencies||{},r=s.lint?.fix?.dependencies||{};let n=!1;try{const a=await pkgTypes.readPackageJSON(t),f={...a.dependencies,...a.devDependencies},u=(await nypm.detectPackageManager(t))?.name||"npm",l=utils.getPackageManagerCommands(u);if(consola.consola.start("Checking dependencies..."),e.blockedPackages&&e.blockedPackages.length>0){const d=Object.keys(f).filter(c=>e.blockedPackages?.includes(c));if(d.length>0)if(o&&r.removeBlocked&&l.remove){consola.consola.start(`Removing blocked packages: ${d.join(", ")}`);try{for(const c of d)node_child_process.execSync(`${l.remove} ${c}`,{cwd:t,stdio:"inherit"});consola.consola.success(`Removed ${d.length} blocked packages`)}catch(c){consola.consola.error("Failed to remove blocked packages:",c),n=!0}}else consola.consola.error(`Blocked packages found: ${d.join(", ")}. Please remove these packages from your dependencies.`),n=!0}if(e.checkOutdated)if(l.outdated)try{node_child_process.execSync(l.outdated,{cwd:t,stdio:"pipe"})}catch(d){if(o&&r.updateOutdated&&l.update){consola.consola.start("Updating outdated dependencies...");try{node_child_process.execSync(l.update,{cwd:t,stdio:"inherit"}),consola.consola.success("Dependencies updated")}catch(c){consola.consola.error("Failed to update dependencies:",c),n=!0}}else consola.consola.warn("Some dependencies are outdated:",d)}else consola.consola.warn(`Outdated check not available for ${u}`);if(e.checkSecurity)if(l.audit)try{node_child_process.execSync(l.audit,{cwd:t,stdio:"pipe"})}catch(d){if(o&&r.fixSecurity&&l.auditFix){consola.consola.start("Fixing security vulnerabilities...");try{node_child_process.execSync(l.auditFix,{cwd:t,stdio:"inherit"}),consola.consola.success("Security fixes applied")}catch(c){consola.consola.error("Failed to fix security issues:",c),n=!0}}else consola.consola.error("Security vulnerabilities detected:",d),n=!0}else consola.consola.warn(`Security audit not available for ${u}`);if(e.allowedLicenses&&e.allowedLicenses.length>0){const{hasIssues:d,invalidLicenses:c}=await S(t,e.allowedLicenses);d&&(consola.consola.error("Packages with invalid licenses found:"),c.forEach(p=>consola.consola.error(` ${p}`)),n=!0)}}catch(a){consola.consola.error("Failed to check dependencies:",a),n=!0}return!n}async function v(t,i,o=!1){if(i.length===0)return!0;const s=(await Promise.all(i.map(async e=>({file:e,exists:await utils.fileExists(pathe.resolve(t,e))})))).filter(e=>!e.exists);if(s.length>0)if(o){consola.consola.start(`Creating missing files: ${s.map(e=>e.file).join(", ")}`);try{for(const{file:e}of s)await promises.writeFile(pathe.resolve(t,e),"","utf8");consola.consola.success(`Created ${s.length} missing files`)}catch(e){return consola.consola.error("Failed to create missing files:",e),!1}}else return s.forEach(({file:e})=>{consola.consola.error(`Required file missing: ${e}`)}),!1;return!0}async function R(t,i,o=!1){if(i.length===0)return!0;const s=(await Promise.all(i.map(async e=>({dir:e,exists:await utils.fileExists(pathe.resolve(t,e))})))).filter(e=>!e.exists);if(s.length>0)if(o){consola.consola.start(`Creating missing directories: ${s.map(e=>e.dir).join(", ")}`);try{for(const{dir:e}of s)await promises.mkdir(pathe.resolve(t,e),{recursive:!0});consola.consola.success(`Created ${s.length} missing directories`)}catch(e){return consola.consola.error("Failed to create missing directories:",e),!1}}else return s.forEach(({dir:e})=>{consola.consola.error(`Required directory missing: ${e}`)}),!1;return!0}async function D(t,i,o){const s=new RegExp(o),e=(await getProjectFiles(t,[i])).filter(r=>{const n=r.split("/").pop()||"";return!s.test(n)});return e.length>0?(consola.consola.error(`Files with invalid naming in ${i}: ${e.slice(0,3).join(", ")}${e.length>3?"...":""}`),!1):!0}async function L(t,i,o){const s=new RegExp(o),e=new Set,r=(await getProjectFiles(t,[i.replace(/\/\*\*?$/,"")])).filter(async n=>{const a=pathe.resolve(t,n);try{return(await import("node:fs/promises").then(f=>f.stat(a))).isDirectory()}catch{return!1}});for(const n of await Promise.all(r))if(n){const a=n.split("/").pop()||"";s.test(a)||e.add(n)}return e.size>0?(consola.consola.error(`Directories with invalid naming in ${i}: ${Array.from(e).slice(0,3).join(", ")}`),!1):!0}async function A(t,i){let o=!1;for(const s of i){const{path:e,files:r,directories:n,description:a}=s;consola.consola.start(`Checking naming rule: ${a||e}`),r&&(await D(t,e,r)||(o=!0)),n&&(await L(t,e,n)||(o=!0))}return!o}async function lintStructure(t=process.cwd(),i,o=!1){const{config:s}=await utils.loadConfig({cwd:t,overrides:i?{lint:{structure:i}}:void 0}),e=s.lint?.structure||{},r=s.lint?.fix?.structure||{};let n=!1;return consola.consola.start("Checking project structure..."),e.requiredFiles&&(await v(t,e.requiredFiles,o&&r.createMissingFiles)||(n=!0)),e.requiredDirs&&(await R(t,e.requiredDirs,o&&r.createMissingDirs)||(n=!0)),e.naming&&e.naming.length>0&&(await A(t,e.naming)||(n=!0)),!n}async function lintDocs(t=process.cwd(),i,o=!1){const{config:s}=await utils.loadConfig({cwd:t,overrides:i?{lint:{docs:i}}:void 0}),e=s.lint?.docs||{},r=s.lint?.fix?.docs||{};let n=!1;consola.consola.start("Checking documentation...");const a=[];e.checkReadme&&a.push({type:"README",files:["README.md","README.rst","README.txt","readme.md"],required:!0,fixEnabled:o&&(r.generateReadme??!1),createFile:"README.md"}),e.checkChangelog&&a.push({type:"CHANGELOG",files:["CHANGELOG.md","CHANGELOG.rst","HISTORY.md","changelog.md"],required:!0,fixEnabled:o&&(r.generateChangelog??!1),createFile:"CHANGELOG.md"});for(const{type:f,files:u,required:l,fixEnabled:d,createFile:c}of a){const p=(await Promise.all(u.map(g=>utils.fileExists(pathe.resolve(t,g))))).some(g=>g);if(l&&!p)if(d&&c){consola.consola.start(`Creating ${c}...`);try{await promises.writeFile(pathe.resolve(t,c),"","utf8"),consola.consola.success(`Created ${c}`)}catch(g){consola.consola.error(`Failed to create ${c}:`,g),n=!0}}else consola.consola.error(`No ${f} file found`),n=!0}return!n}async function lintAll(t=process.cwd(),i=!1){const{config:o}=await utils.loadConfig({cwd:t}),s=o.lint||{};consola.consola.start("Running comprehensive project lint...");const e=(await Promise.allSettled([lintProject(t,s.project),lintDependencies(t,s.dependencies,i),lintStructure(t,s.structure,i),lintDocs(t,s.docs,i)])).filter(r=>r.status==="rejected"||r.status==="fulfilled"&&!r.value);return e.length===0?!0:(consola.consola.error(`${e.length} lint check(s) failed`),!1)}async function S(t,i){try{const o=await P__default(["node_modules/*/package.json","node_modules/@*/*/package.json"],{cwd:t,onlyFiles:!0,absolute:!0}),s=[],e=new Set;for(const r of o)try{const n=await pkgTypes.readPackageJSON(r);if(!n.name||e.has(n.name))continue;if(e.add(n.name),n.license){const a=Array.isArray(n.license)?n.license.join(", "):n.license;i.some(f=>a.includes(f))||s.push(`${n.name}: ${a}`)}}catch{}return{hasIssues:s.length>0,invalidLicenses:s}}catch(o){return consola.consola.warn("Failed to check licenses:",o),{hasIssues:!1,invalidLicenses:[]}}}exports.getProjectFiles=getProjectFiles,exports.getStagedFiles=getStagedFiles,exports.lintAll=lintAll,exports.lintDependencies=lintDependencies,exports.lintDocs=lintDocs,exports.lintProject=lintProject,exports.lintStaged=lintStaged,exports.lintStructure=lintStructure;