@kitschpatrol/mdat-config
Version:
MDAT configuration for @kitschpatrol/shared-config.
7 lines • 12 kB
JavaScript
import{cosmiconfig as e}from"cosmiconfig";import{TypeScriptLoader as t}from"cosmiconfig-typescript-loader";import{execa as n}from"execa";import r from"fs-extra";import i from"node:fs";import a from"node:path";import{PassThrough as o,Transform as s}from"node:stream";import{fileURLToPath as c}from"node:url";import{packageUp as l,packageUpSync as u}from"package-up";import d from"picocolors";import f from"yargs";import{hideBin as p}from"yargs/helpers";import m from"@pinojs/json-colorizer";import h from"decircular";import g from"deepmerge";import _ from"json-stringify-pretty-compact";import{findWorkspaces as v,findWorkspacesRoot as y}from"find-workspaces";import b from"node:fs/promises";import{stripVTControlCharacters as x}from"node:util";import{loadConfig as S}from"mdat";var C=`7.6.2`;function w(e){return e instanceof Error&&`exitCode`in e&&typeof e.exitCode==`number`}function T(e){return m(_(h(e),{indent:2,replacer(e,t){return typeof t==`function`?t.name:t}}),{colors:{BRACKET:`gray`}})}const E=(e,t,n)=>{let r=[...e];for(let[i,a]of t.entries())r[i]===void 0?r[i]=n.cloneUnlessOtherwiseSpecified(a,n):n.isMergeableObject(a)?r[i]=D(e[i],a,n):e.includes(a)||r.push(a);return r};function D(e,t,n={arrayMerge:E}){return g(e,t,n)}function O(e,t){return e.startsWith(t+a.sep)}function k(){let e=A(),t=new Set([e]),n=v();if(n!==null)for(let r of n){let n=a.resolve(r.location);O(n,e)&&t.add(n)}return[...t]}function A(){let e=u();if(e===void 0)throw Error(`No package.json found.`);return a.dirname(e)}function j(){let e=y();return e===null?A():a.resolve(e.location)}function M(e){if(e===`workspace-root`)return j();if(e===`package-dir`)return A();if(typeof e==`string`){if(!r.pathExistsSync(e))throw Error(`Custom cwd directory does not exist: ${e}`);return e}return process.cwd()}async function ee(e,t){try{let{default:n}=await import(`prettier`),r=await n.resolveConfig(e),i=await n.format(t,{filepath:e,...r});await b.writeFile(e,i,`utf8`)}catch{console.warn(`Skipped formatting ${e} since Prettier is not installed.`)}}async function N(e){try{await ee(e,await b.readFile(e,`utf8`))}catch{}}const P=/\r?\n/;function F(e){return new s({transform(t,n,r){let i=t.toString().split(P).filter(t=>t.trim()!==``&&!e(x(t))).join(`
`);this.push(i+`
`),r()}})}function I(e,t){return new s({transform(n,r,i){let a=n.toString().split(P).filter(e=>e.trim().length>0).map(n=>`${e?t===void 0?e:d[t](e):``} ${n}\n`).join(``);this.push(a),i()}})}async function L(e){let t=[];return new Promise((n,r)=>{e.on(`data`,e=>t.push(e)),e.on(`error`,e=>{r(e)}),e.on(`end`,()=>{n(Buffer.concat(t).toString(`utf8`))})})}function R(e,t){return t===1?e:e+`s`}async function z(e,t,n,r,i){let a=1,o;if(r.logPrefix===void 0)o=e;else{let t=I(r.logPrefix,r.logColor);t.pipe(e),o=t}i&&o.write(d.bold(`Running: "${r.name}() with Positional arguments: ${String(t)} and Option flags: ${String(n)}"`));try{a=await r.execute(o,t,n)}catch(e){console.error(String(e)),a=1}return a}async function B(e,t,r,i,a){let s=1,c;if(i.logPrefix===void 0)c=e;else{let t=I(i.logPrefix,i.logColor);t.pipe(e),c=t}let l=i.subcommands??[],u=[...i.receivePositionalArguments?t:[],...i.positionalArguments??[]],d=[...i.receiveOptionFlags?r:[],...i.optionFlags??[]],f=[...l,...d,...u],p=M(i.cwdOverride);a&&c.write(`Running: "${i.name} ${f.join(` `)}"`);let m=i.prettyJsonOutput?new o:c;try{let e=n(i.name,f,{cwd:p,env:{...process.env.NO_COLOR===void 0?{FORCE_COLOR:`true`}:{}},preferLocal:!0,reject:!1,stdin:`inherit`});if(i.outputFilter){let t=F(i.outputFilter),n=F(i.outputFilter);e.stdout.pipe(t).pipe(m,{end:!1}),e.stderr.pipe(n).pipe(m,{end:!1})}else e.stdout.pipe(m,{end:!1}),e.stderr.pipe(m,{end:!1});if(await e,i.prettyJsonOutput){m.end();let e=await L(m),t=T(JSON.parse(e)).split(`
`);for(let e of t)c.write(`${e}\n`)}s=e.exitCode??1}catch(e){console.error(`${i.name} failed with error:`),console.error(e),w(e)&&(s=typeof e.exitCode==`number`?e.exitCode:1)}return s}function V(e){return`execute`in e}const H=/^ksc-/;function U(e){return e.replace(H,``)}function W(e){return e===void 0||e.length===0?[]:e.flatMap(e=>e.split(`,`)).map(e=>U(e.trim()))}function G(e){return e.option(`skip`,{array:!0,describe:`Tool names to skip (with or without "ksc-" prefix).`,type:`string`})}async function K(e,t,n,r,i,a,o){let s=o??[],c=[],l=[];for(let e of r)s.length>0&&s.includes(U(e.name))?l.push(e):c.push(e);if(s.length>0){let t=new Set(l.map(e=>U(e.name))),n=s.filter(e=>!t.has(e));if(n.length>0){let t=r.map(e=>U(e.name)).join(`, `);e.write(`⚠️ ${d.yellow(`Unrecognized --skip ${R(`value`,n.length)}: ${n.join(`, `)}. Available: ${t}`)}\n`)}}let u=[];for(let r of c){let a=await(V(r)?z(e,t,n,r,i):B(e,t,n,r,i));u.push({exitCode:a,name:r.name})}let f=r.length;if(l.length>0){let t=l.map(({name:e})=>e);e.write(`⏭️ ${d.dim(d.bold(`${t.length} / ${f} ${R(`Command`,t.length)} Skipped:`))} ${d.dim(t.join(`, `))}\n`)}if(a){let t=u.filter(({exitCode:e})=>e===0).map(({name:e})=>e),n=u.filter(({exitCode:e})=>e!==0).map(({name:e})=>e);t.length>0&&e.write(`✅ ${d.green(d.bold(`${t.length} / ${f} ${R(`Command`,t.length)} Succeeded:`))} ${d.green(t.join(`, `))}\n`),n.length>0&&e.write(`❌ ${d.red(d.bold(`${n.length} / ${f} ${R(`Command`,n.length)} Failed:`))} ${d.red(n.join(`, `))}\n`)}return+!u.every(({exitCode:e})=>e===0)}async function q(e,t,n,o){let s=await l();if(s===void 0)throw Error("The `init` command must be used in a directory with a package.json file");let u=await l({cwd:c(import.meta.url)});if(u===void 0)return e.write(`Error: The script being called was not in a package, weird.
`),1;let d=a.join(a.dirname(u),`init`),f=a.dirname(s),p=(t===`file`||t===`package`)&&n!==void 0&&o!==void 0;try{if(p){let n=Object.keys(o)[0];if(t===`package`){let t=r.readJsonSync(s);e.write(`Merging: \nPackage config key "${n}" → "${f}" (Because --location is set to "package")\n`);let i=D(t,o);r.writeJSONSync(s,i,{spaces:` `}),await N(s)}else{let t=r.readJsonSync(s);Object.keys(t).includes(n)&&(e.write(`Deleting: \nPackage config key "${n}" in "${f}" (Because --location is set to "file")\n`),delete t[n],r.writeJSONSync(s,t,{spaces:` `}),await N(s))}}if(!await r.pathExists(d))return 0;if((await r.readdir(d)).length===0)return e.write(`Source directory "${d}" is empty.\n`),0;e.write(`Adding initial configuration files from:\n"${d}" → "${f}"\n`),await r.copy(d,f,{async filter(o,s){let c=i.statSync(o).isFile(),l=i.existsSync(s);if(c){if(p&&t===`package`&&o.includes(n))return l?(e.write(`Deleting: \n"${o}" → "${s}" (Because --location is set to "package")\n`),r.removeSync(s)):e.write(`Skipping: \n"${o}" → "${s}" (Because --location is set to "package")\n`),!1;if(l&&(s.includes(`.vscode/`)||s.includes(`package.json`))&&a.extname(s)===`.json`){e.write(`Merging: \n"${o}" → "${s}"\n`);let t=r.readJSONSync(o),n=D(r.readJSONSync(s),t);return r.writeJSONSync(s,n,{spaces:` `}),await N(s),!1}return l?(e.write(`Overwriting: \n"${o}" → "${s}"\n`),await N(s),!0):(e.write(`Copying: \n"${o}" → "${s}"\n`),await N(s),!0)}return!0},overwrite:!0})}catch(e){return console.error(String(e)),1}return 0}async function J(e){let{commands:{fix:t,init:n,lint:r,printConfig:i},description:a,logColor:o,logPrefix:s,name:c,showSummary:l,verbose:u}=e,d=I(s,o);d.pipe(process.stdout);let m=f(p(process.argv)).scriptName(c).usage(`$0 <command>`,a);n!==void 0&&m.command({builder(e){let t=n.locationOptionFlag?e.option(`location`,{choices:[`file`,`package`],default:`file`,describe:`Where to store the configuration.`,type:`string`}):e;return l?G(t):t},command:`init`,describe:n.description??`Initialize by copying starter config files to your project root${n.locationOptionFlag?` or to your package.json file.`:`.`}`,async handler(e){let t=n.locationOptionFlag?e.location:void 0,r=W(e.skip),i=await K(d,[],t===void 0?[]:[`--location`,t],[{async execute(e,t,r){return q(e,r.at(1),n.configFile,n.configPackageJson)},name:`copyAndMergeInitFiles`},...n.commands??[]],void 0,void 0,r);process.exit(i)}}),r!==void 0&&m.command({builder(e){let t=r.positionalArgumentMode===`none`?e:e.positional(`files`,{array:!0,...r.positionalArgumentDefault===void 0?{}:{default:r.positionalArgumentDefault},describe:`Files or glob pattern to lint.`,type:`string`});return l?G(t):t},command:r.positionalArgumentMode===`none`?`lint`:r.positionalArgumentMode===`optional`?`lint [files..]`:`lint <files..>`,describe:r.description,async handler(e){let t=e.files??[],n=W(e.skip),i=await K(d,t,[],r.commands,u,l,n);process.exit(i)}}),t!==void 0&&m.command({builder(e){let n=t.positionalArgumentMode===`none`?e:e.positional(`files`,{array:!0,...t.positionalArgumentDefault===void 0?{}:{default:t.positionalArgumentDefault},describe:`Files or glob pattern to fix.`,type:`string`});return l?G(n):n},command:t.positionalArgumentMode===`none`?`fix`:t.positionalArgumentMode===`optional`?`fix [files..]`:`fix <files..>`,describe:t.description,async handler(e){let n=e.files??[],r=W(e.skip),i=await K(d,n,[],t.commands,void 0,void 0,r);process.exit(i)}}),i!==void 0&&m.command({builder(e){let t=i.positionalArgumentMode===`none`?e:e.positional(`file`,{...i.positionalArgumentDefault===void 0?{}:{default:i.positionalArgumentDefault},describe:`File or glob pattern to print configuration for.`,type:`string`});return l?G(t):t},command:i.positionalArgumentMode===`none`?`print-config`:i.positionalArgumentMode===`optional`?`print-config [file]`:`print-config <file>`,describe:i.description,async handler(e){let t=e.file??void 0,n=t===void 0?[]:[t],r=W(e.skip),a=await K(d,n,[],i.commands,u,l,r);process.exit(a)}}),m.alias(`h`,`help`),m.version(C),m.alias(`v`,`version`),m.help(),m.wrap(process.stdout.isTTY?Math.min(120,m.terminalWidth()):0),await m.parseAsync()}async function Y(n){let r=e(n,{loaders:{".ts":t()},searchStrategy:`project`});try{let e=await r.search();if(e===null){console.error(`No ${n} configuration found.`);return}return e}catch(e){console.error(`Error while searching for ${n} configuration:`,e);return}}const X={fileRun:`Matches files below the current working directory by default.`,monorepoRun:`In a monorepo, it will also run in all packages below the current working directory.`,monorepoSearch:`Searches up to the root of a monorepo if necessary.`,multiArgumentCaveat:`Will use file arguments / globs where possible if provided, but some of the invoked tools only operate at the package scope.`,multiOptionCaveat:`Will use option flags where possible if provided, but some of the invoked tools will ignore them.`,optionalFileRun:`Package-scoped by default, file-scoped if a file argument is provided.`,packageRun:`Package-scoped.`,packageSearch:`Package-scoped.`};async function Z(){return(await Y(`mdat`))?.filepath}async function Q(e){let t=`mdat`,n=await Y(t);n!==void 0&&e.write(`Found ${t} readme configuration at "${n.filepath}"\n`);let r=T(await S({additionalConfig:await S()})).split(`
`);for(let t of r)e.write(`${t}\n`);return 0}async function $(e){let t=k(),n=await Z(),r=[];for(let i of t)r.push({cwdOverride:i,name:`mdat`,optionFlags:n?[`--config`,n,`--format`]:[`--format`],subcommands:[e]});return r}await J({commands:{fix:{commands:await $(`expand`),description:`Expand all Mdat content placeholders in your readme.md file(s). ${X.packageRun} ${X.monorepoRun}`,positionalArgumentMode:`none`},init:{configFile:`mdat.config.ts`,configPackageJson:{mdat:{$import:`node_modules/@kitschpatrol/mdat-config/dist/index.js`}},locationOptionFlag:!0},lint:{commands:await $(`check`),description:`Validate that all Mdat content placeholders in your readme.md file(s) have been expanded and are up to date. ${X.packageRun} ${X.monorepoRun}`,positionalArgumentMode:`none`},printConfig:{commands:[{execute:Q,name:Q.name}],description:`Print the effective Mdat configuration. ${X.packageSearch}. ${X.monorepoSearch}.`,positionalArgumentMode:`none`}},description:`Kitschpatrol's Mdat shared configuration tools.`,logColor:`green`,logPrefix:`[Mdat Config]`,name:`ksc-mdat`,order:2});export{};