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