@kitschpatrol/shared-config
Version:
A collection of shared configurations, linters, and formatting tools for TypeScript projects. All managed as a single dependency, and invoked via a single CLI command.
13 lines • 30.5 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,{constants as a}from"node:fs";import o from"node:path";import{PassThrough as s,Transform as c}from"node:stream";import{fileURLToPath as l}from"node:url";import{packageUp as u,packageUpSync as d}from"package-up";import f from"picocolors";import p from"yargs";import{hideBin as m}from"yargs/helpers";import h from"@pinojs/json-colorizer";import g from"decircular";import _ from"deepmerge";import ee from"json-stringify-pretty-compact";import{findWorkspaces as te,findWorkspacesRoot as ne}from"find-workspaces";import v,{access as re}from"node:fs/promises";import{stripVTControlCharacters as y}from"node:util";import{getDefaultConfigLoader as b,resolveConfigFileImports as x}from"cspell-lib";import{lint as ie}from"cspell";import"deepmerge-ts";import{loadConfig as S}from"mdat";import{globby as ae}from"globby";import C,{gt as w,minVersion as oe}from"semver";import{readWantedLockfile as se}from"@pnpm/lockfile.fs";import ce from"stylelint";var le=`7.6.2`;function ue(e){return e instanceof Error&&`exitCode`in e&&typeof e.exitCode==`number`}function T(e){return h(ee(g(e),{indent:2,replacer(e,t){return typeof t==`function`?t.name:t}}),{colors:{BRACKET:`gray`}})}const de=(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]=E(e[i],a,n):e.includes(a)||r.push(a);return r};function E(e,t,n={arrayMerge:de}){return _(e,t,n)}function fe(e,t){return e.startsWith(t+o.sep)}function pe(){let e=D(),t=new Set([e]),n=te();if(n!==null)for(let r of n){let n=o.resolve(r.location);fe(n,e)&&t.add(n)}return[...t]}function D(){let e=d();if(e===void 0)throw Error(`No package.json found.`);return o.dirname(e)}function me(){return ne()!==null}function O(){let e=ne();return e===null?D():o.resolve(e.location)}function k(e){let t=o.join(O(),e);if(r.existsSync(t))return t}function A(e){if(e===`workspace-root`)return O();if(e===`package-dir`)return D();if(typeof e==`string`){if(!r.pathExistsSync(e))throw Error(`Custom cwd directory does not exist: ${e}`);return e}return process.cwd()}async function he(e,t){try{let{default:n}=await import(`prettier`),r=await n.resolveConfig(e),i=await n.format(t,{filepath:e,...r});await v.writeFile(e,i,`utf8`)}catch{console.warn(`Skipped formatting ${e} since Prettier is not installed.`)}}async function j(e){try{await he(e,await v.readFile(e,`utf8`))}catch{}}const ge=/\r?\n/;function M(e){return new c({transform(t,n,r){let i=t.toString().split(ge).filter(t=>t.trim()!==``&&!e(y(t))).join(`
`);this.push(i+`
`),r()}})}function N(e,t){return new c({transform(n,r,i){let a=n.toString().split(ge).filter(e=>e.trim().length>0).map(n=>`${e?t===void 0?e:f[t](e):``} ${n}\n`).join(``);this.push(a),i()}})}async function _e(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 ve(e){return e.replaceAll(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,e=>`-`+e.toLowerCase())}function P(e,t){return t===1?e:e+`s`}async function ye(e,t,n,r,i){let a=1,o;if(r.logPrefix===void 0)o=e;else{let t=N(r.logPrefix,r.logColor);t.pipe(e),o=t}i&&o.write(f.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 be(e,t,r,i,a){let o=1,c;if(i.logPrefix===void 0)c=e;else{let t=N(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=A(i.cwdOverride);a&&c.write(`Running: "${i.name} ${f.join(` `)}"`);let m=i.prettyJsonOutput?new s: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=M(i.outputFilter),n=M(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 _e(m),t=T(JSON.parse(e)).split(`
`);for(let e of t)c.write(`${e}\n`)}o=e.exitCode??1}catch(e){console.error(`${i.name} failed with error:`),console.error(e),ue(e)&&(o=typeof e.exitCode==`number`?e.exitCode:1)}return o}function xe(e){return`execute`in e}const Se=/^ksc-/;function F(e){return e.replace(Se,``)}function I(e){return e===void 0||e.length===0?[]:e.flatMap(e=>e.split(`,`)).map(e=>F(e.trim()))}function L(e){return e.option(`skip`,{array:!0,describe:`Tool names to skip (with or without "ksc-" prefix).`,type:`string`})}async function R(e,t,n,r,i,a,o){let s=o??[],c=[],l=[];for(let e of r)s.length>0&&s.includes(F(e.name))?l.push(e):c.push(e);if(s.length>0){let t=new Set(l.map(e=>F(e.name))),n=s.filter(e=>!t.has(e));if(n.length>0){let t=r.map(e=>F(e.name)).join(`, `);e.write(`⚠️ ${f.yellow(`Unrecognized --skip ${P(`value`,n.length)}: ${n.join(`, `)}. Available: ${t}`)}\n`)}}let u=[];for(let r of c){let a=await(xe(r)?ye(e,t,n,r,i):be(e,t,n,r,i));u.push({exitCode:a,name:r.name})}let d=r.length;if(l.length>0){let t=l.map(({name:e})=>e);e.write(`⏭️ ${f.dim(f.bold(`${t.length} / ${d} ${P(`Command`,t.length)} Skipped:`))} ${f.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(`✅ ${f.green(f.bold(`${t.length} / ${d} ${P(`Command`,t.length)} Succeeded:`))} ${f.green(t.join(`, `))}\n`),n.length>0&&e.write(`❌ ${f.red(f.bold(`${n.length} / ${d} ${P(`Command`,n.length)} Failed:`))} ${f.red(n.join(`, `))}\n`)}return+!u.every(({exitCode:e})=>e===0)}async function Ce(e,t,n,a){let s=await u();if(s===void 0)throw Error("The `init` command must be used in a directory with a package.json file");let c=await u({cwd:l(import.meta.url)});if(c===void 0)return e.write(`Error: The script being called was not in a package, weird.
`),1;let d=o.join(o.dirname(c),`init`),f=o.dirname(s),p=(t===`file`||t===`package`)&&n!==void 0&&a!==void 0;try{if(p){let n=Object.keys(a)[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=E(t,a);r.writeJSONSync(s,i,{spaces:` `}),await j(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 j(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(a,s){let c=i.statSync(a).isFile(),l=i.existsSync(s);if(c){if(p&&t===`package`&&a.includes(n))return l?(e.write(`Deleting: \n"${a}" → "${s}" (Because --location is set to "package")\n`),r.removeSync(s)):e.write(`Skipping: \n"${a}" → "${s}" (Because --location is set to "package")\n`),!1;if(l&&(s.includes(`.vscode/`)||s.includes(`package.json`))&&o.extname(s)===`.json`){e.write(`Merging: \n"${a}" → "${s}"\n`);let t=r.readJSONSync(a),n=E(r.readJSONSync(s),t);return r.writeJSONSync(s,n,{spaces:` `}),await j(s),!1}return l?(e.write(`Overwriting: \n"${a}" → "${s}"\n`),await j(s),!0):(e.write(`Copying: \n"${a}" → "${s}"\n`),await j(s),!0)}return!0},overwrite:!0})}catch(e){return console.error(String(e)),1}return 0}async function we(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=N(s,o);d.pipe(process.stdout);let f=p(m(process.argv)).scriptName(c).usage(`$0 <command>`,a);n!==void 0&&f.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?L(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=I(e.skip),i=await R(d,[],t===void 0?[]:[`--location`,t],[{async execute(e,t,r){return Ce(e,r.at(1),n.configFile,n.configPackageJson)},name:`copyAndMergeInitFiles`},...n.commands??[]],void 0,void 0,r);process.exit(i)}}),r!==void 0&&f.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?L(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=I(e.skip),i=await R(d,t,[],r.commands,u,l,n);process.exit(i)}}),t!==void 0&&f.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?L(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=I(e.skip),i=await R(d,n,[],t.commands,void 0,void 0,r);process.exit(i)}}),i!==void 0&&f.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?L(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=I(e.skip),a=await R(d,n,[],i.commands,u,l,r);process.exit(a)}}),f.alias(`h`,`help`),f.version(le),f.alias(`v`,`version`),f.help(),f.wrap(process.stdout.isTTY?Math.min(120,f.terminalWidth()):0),await f.parseAsync()}function z(e){return{async execute(t){let n=await B(e);if(n===void 0)return 1;let{config:r,filepath:i,isEmpty:a}=n;if(t.write(`Found ${e} configuration at "${i}"\n`),a)return t.write(`Configuration is empty.
`),0;let o=T(r).split(`
`);for(let e of o)t.write(`${e}\n`);return 0},name:`Cosmiconfig ${e}`}}async function B(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 V={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.`},Te=/['\u2019\u2018]s$/;async function Ee(e=[`.`]){let t=await b().searchForConfigFile(void 0);if(t===void 0)throw Error(`No CSpell configuration found.`);let{settings:n,url:r}=t;if(n.words===void 0||n.words.length===0)return[];let i=[...n.words];return n.words=void 0,await ie(e,{config:{settings:n,url:r},progress:!1,unique:!0,wordsOnly:!0},{issue({text:e,uri:t}){i=i.filter(n=>n.toLowerCase()!==e.toLowerCase().replace(Te,``)||t===r.href)}}),i}async function De(){let e=await b().searchForConfigFile(void 0);if(e===void 0)throw Error(`No CSpell configuration found.`);let t=await x(e);if(t.ignorePaths===void 0)return``;let n=[];for(let e of t.ignorePaths)n.push(typeof e==`string`?e:e.glob);return n.join(`,`)}async function H(e,t){let n=await Ee(t);if(n.length>0){let t=N(`[Unused Words]`,`cyanBright`);t.pipe(e);for(let e of n)t.write(`${e}\n`);return 1}return 0}async function Oe(){let e=import.meta.resolve(`@kitschpatrol/cspell-config`),t=await u({cwd:o.dirname(l(e))});if(t===void 0)throw Error(`Could not find Case Police dictionary parent package.`);let n=o.join(o.dirname(t),`dictionaries`,`case-police.json`);try{await re(n,a.F_OK)}catch{throw Error(`Case Police dictionary file "${n}" does not exist.`)}return n}async function U(e,t){let n=`[Case Police]`,r=M(e=>{let t=y(e);return!t.startsWith(n)||!t.includes(`→`)});return r.pipe(e),R(r,t,[],[{logColor:`cyanBright`,logPrefix:n,name:`case-police`,optionFlags:[`--dict`,await Oe(),`--ignore`,await De()],receivePositionalArguments:!0}])}async function W(e){let t=await b().searchForConfigFile(void 0);if(t===void 0)throw Error(`No CSpell configuration found.`);e.write(`Found cspell readme configuration at "${l(t.url)}"\n`);let n=T(await x(t)).split(`
`);for(let t of n)e.write(`${t}\n`);return 0}const ke={commands:{init:{configFile:`cspell.config.ts`,configPackageJson:{cspell:{import:`@kitschpatrol/cspell-config`}},locationOptionFlag:!0},lint:{commands:[{name:`cspell`,optionFlags:[`--quiet`],receivePositionalArguments:!0},{execute:H,name:H.name},{execute:U,name:U.name}],description:`Check for spelling mistakes. ${V.fileRun}`,positionalArgumentDefault:`**/*`,positionalArgumentMode:`optional`},printConfig:{commands:[{execute:W,name:W.name}],description:`Print the resolved CSpell configuration. ${V.packageSearch} ${V.monorepoSearch}`,positionalArgumentMode:`none`}},description:`Kitschpatrol's CSpell shared configuration tools. (Automated fixes are handled by ESLint.)`,logColor:`cyan`,logPrefix:`[CSpell]`,name:`ksc-cspell`,order:6};async function G(e,t){let n;if(t.length>0){let r=o.join(process.cwd(),t[0]);e.write(`Showing configuration for file: ${r}\n`),n={name:`eslint`,optionFlags:[`--print-config`],receivePositionalArguments:!0}}else n=z(`eslint`);return R(e,t,[],[n])}const Ae={commands:{fix:{commands:[{name:`eslint`,optionFlags:[`--fix`],receivePositionalArguments:!0}],description:`Fix your project with ESLint. ${V.fileRun}`,positionalArgumentDefault:`.`,positionalArgumentMode:`optional`},init:{locationOptionFlag:!1},lint:{commands:[{name:`eslint`,optionFlags:[`--max-warnings`,`0`],receivePositionalArguments:!0}],description:`Lint your project with ESLint. ${V.fileRun}`,positionalArgumentDefault:`.`,positionalArgumentMode:`optional`},printConfig:{commands:[{execute:G,name:G.name}],description:`Print the effective ESLint configuration. ${V.optionalFileRun} Use \`@eslint/config-inspector\` for a more detailed view.`,positionalArgumentMode:`optional`}},description:`Kitschpatrol's ESLint shared configuration tools.`,logColor:`magenta`,logPrefix:`[ESLint]`,name:`ksc-eslint`,order:4},je={entry:[`{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!`,`src/{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!`,`src/{bin,lib,cli}/{index,cli,main}.{js,mjs,cjs,jsx,ts,tsx,mts,cts}!`,`scripts/**/*.{js,mjs,cjs,ts,mts,cts}`,`.remarkrc.{js,mjs,cjs,ts,mts,cts}`,`cspell.config.{js,mjs,cjs,ts,mts,cts}`,`eslint.config.{js,mjs,cjs,ts,mts,cts}`,`mdat.config.{js,mjs,cjs,ts,mts,cts}`,`prettier.config.{js,mjs,cjs,ts,mts,cts}`,`stylelint.config.{js,mjs,cjs,ts,mts,cts}`],ignoreBinaries:[`ksc-repo`,`ksc-mdat`,`ksc-typescript`,`ksc-eslint`,`ksc-stylelint`,`ksc-cspell`,`ksc-knip`,`ksc-remark`,`ksc-prettier`,`op`,`gh`],ignoreDependencies:[`@kitschpatrol/cspell-config`,`@kitschpatrol/dict-en-wiktionary`,`@kitschpatrol/eslint-config`,`@kitschpatrol/knip-config`,`@kitschpatrol/mdat-config`,`@kitschpatrol/prettier-config`,`@kitschpatrol/remark-config`,`@kitschpatrol/repo-config`,`@kitschpatrol/stylelint-config`,`@kitschpatrol/typescript-config`,`@prettier/plugin-php`,`@prettier/plugin-ruby`,`@prettier/plugin-xml`,`prettier-plugin-packagejson`,`prettier-plugin-sh`,`prettier-plugin-tailwindcss`,`prettier-plugin-toml`,`remark-attribute-list`,`remark-directive`]};function Me(){if(me()){let e=D(),t=O();if(e!==t){let n=o.relative(t,e);if(n)return[`--workspace`,n.split(o.sep).join(`/`)]}}return[]}function Ne(){return{knip:je}}const Pe={commands:{init:{configFile:`knip.config.ts`,configPackageJson:Ne(),locationOptionFlag:!0},lint:{commands:[{cwdOverride:`workspace-root`,name:`knip`,optionFlags:[`--no-progress`,`--no-config-hints`,...Me()]}],description:`Check for unused code and dependencies. ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},printConfig:{commands:[z(`knip`)],description:`Print the effective Knip configuration. ${V.packageSearch} ${V.monorepoSearch}`,positionalArgumentMode:`none`}},description:`Kitschpatrol's Knip shared configuration tools.`,logColor:`cyan`,logPrefix:`[Knip]`,name:`ksc-knip`,order:7};async function Fe(){return(await B(`mdat`))?.filepath}async function K(e){let t=`mdat`,n=await B(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 q(e){let t=pe(),n=await Fe(),r=[];for(let i of t)r.push({cwdOverride:i,name:`mdat`,optionFlags:n?[`--config`,n,`--format`]:[`--format`],subcommands:[e]});return r}const Ie={commands:{fix:{commands:await q(`expand`),description:`Expand all Mdat content placeholders in your readme.md file(s). ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},init:{configFile:`mdat.config.ts`,configPackageJson:{mdat:{$import:`node_modules/@kitschpatrol/mdat-config/dist/index.js`}},locationOptionFlag:!0},lint:{commands:await q(`check`),description:`Validate that all Mdat content placeholders in your readme.md file(s) have been expanded and are up to date. ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},printConfig:{commands:[{execute:K,name:K.name}],description:`Print the effective Mdat configuration. ${V.packageSearch}. ${V.monorepoSearch}.`,positionalArgumentMode:`none`}},description:`Kitschpatrol's Mdat shared configuration tools.`,logColor:`green`,logPrefix:`[Mdat Config]`,name:`ksc-mdat`,order:2},J=[`--log-level=warn`,`--plugin=@kitschpatrol/prettier-plugin-astro`,`--plugin=@prettier/plugin-php`,`--plugin=@prettier/plugin-ruby`,`--plugin=@prettier/plugin-xml`,`--plugin=prettier-plugin-jsdoc`,`--plugin=prettier-plugin-packagejson`,`--plugin=prettier-plugin-sh`,`--plugin=prettier-plugin-svelte`,`--plugin=prettier-plugin-toml`,`--ignore-path=${k(`.gitignore`)??`.gitignore`}`,`--ignore-path=${k(`.prettierignore`)??`.prettierignore`}`],Le={commands:{fix:{commands:[{name:`prettier`,optionFlags:[...J,`--write`],receivePositionalArguments:!0}],description:`Format files according to your Prettier configuration. ${V.fileRun}`,positionalArgumentDefault:`.`,positionalArgumentMode:`optional`},init:{configFile:`prettier.config.ts`,configPackageJson:{prettier:`@kitschpatrol/prettier-config`},locationOptionFlag:!0},lint:{commands:[{name:`prettier`,optionFlags:[...J,`--check`],receivePositionalArguments:!0}],description:`Check that files are formatted according to your Prettier configuration. ${V.fileRun}`,positionalArgumentDefault:`.`,positionalArgumentMode:`optional`},printConfig:{commands:[z(`prettier`)],description:`Print the effective Prettier configuration. ${V.packageSearch}. ${V.monorepoSearch}.`,positionalArgumentMode:`none`}},description:`Kitschpatrol's Prettier shared configuration tools.`,logColor:`blue`,logPrefix:`[Prettier]`,name:`ksc-prettier`,order:9},Re={commands:{init:{configFile:`.remarkrc.js`,configPackageJson:{remarkConfig:{plugins:[`@kitschpatrol/remark-config`]}},locationOptionFlag:!0},printConfig:{commands:[z(`remark`)],description:`Print the effective Remark configuration. ${V.packageSearch} ${V.monorepoSearch}`,positionalArgumentMode:`none`}},description:`Kitschpatrol's Remark and Remark Lint shared configuration tools. (Actual linting and fixing is managed through @kitschpatrol/eslint-config.)`,logColor:`blue`,logPrefix:`[remarklint]`,name:`ksc-remark`,order:8},ze=[/Version 2\.0,?\s*January 2004/g,/Version 1,?\s*February 1989/g,/Version 2,?\s*June 1991/g,/Version 3,?\s*29 June 2007/g,/Version 2\.1,?\s*February 1999/g,/Version 3,?\s*19 November 2007/g,/Version 1\.0\s*-\s*August 17th,?\s*2003/g,/Version 1\.1,?\s*March 2000/g,/Version 1\.2,?\s*November 2002/g,/Version 1\.3,?\s*3 November 2008/g,/Version 1\.0\s*-\s*22 November 2005/g,/Version 1\.1\s*-\s*26 February 2007/g,/Version 2,?\s*December 2004/g,/20 December 1996/g,/December 20,?\s*1996/g,/11 March 1996/g,/28 March 2007/g,/November 1,?\s*2008/g,/August 1,?\s*2009/g,/1 April 19\d{2}/g,/Copyright \(c\) 2000-2006,?\s*The Perl Foundation/gi,/Copyright \(C\) (?:\d{4},?\s*)+Free Software Foundation/g],Be=[`node_modules/**`,`test/**`];function Ve(e){let t=e;for(let e of ze)e.lastIndex=0,t=t.replace(e,e=>e.replaceAll(/\d/g,`X`));return t}const He=/(\d{4})\s*-\s*(\d{4})/,Ue=/(\d{4})/;function We(e,t){let n=Ve(e),r=He,i=r.exec(n);if(i){let[,n,a]=i;if(Number.parseInt(a,10)!==t){let a=`${n}-${t}`;return e.slice(0,i.index)+e.slice(i.index).replace(r,a)}return e}let a=Ue,o=a.exec(n);if(o){let[,n]=o;if(Number.parseInt(n,10)!==t){let r=`${n}-${t}`;return e.slice(0,o.index)+e.slice(o.index).replace(a,r)}return e}return e}async function Y(e,t=!1){let n=new Date().getFullYear(),r=[],i=await ae([`**/license.txt`,`**/license`,...Be.map(e=>`!${e}`)],{caseSensitiveMatch:!1,cwd:D(),followSymbolicLinks:!1,gitignore:!0});for(let e of i)r.push(e);let a=[];for(let e of r)try{let r=await v.readFile(e,`utf8`),i=We(r,n);i!==r&&(a.push(e),t&&await v.writeFile(e,i,`utf8`))}catch(t){console.error(`Failed to process ${e}:`,t)}if(a.length>0){e.write(`${t?`Fixed`:`Found`} ${a.length} license ${P(`file`,a.length)} with outdated copyright year:\n`);for(let t of a)e.write(` - ${t}\n`);return+!t}return 0}async function Ge(e){return Y(e,!1)}async function Ke(e){return Y(e,!0)}const X=`pnpm-lock.yaml`;function qe(e){let t=O(),n=o.resolve(e);for(;;){if(r.existsSync(o.join(n,X)))return n;if(n===t)break;let e=o.dirname(n);if(e===n)break;n=e}}function Je(e,t,n){let r=`${e}@${t}`;if(r in n)return r;if(t in n)return t}async function Ye(e){let t=qe(e);if(!t)throw Error(`${X} not found at or above "${e}".`);let n=o.join(t,X),r=await se(t,{ignoreIncompatible:!1});if(!r?.importers)throw Error(`Lockfile at "${n}" is unreadable or missing importers.`);let i=r.packages??{},a,s,c={},l={};function u(e,t){let n,r=new Set;function a(e,t){if(t.startsWith(`link:`))return;let o=Je(e,t,i);if(!o||r.has(o))return;r.add(o);let s=i[o],c=s?.engines?.node;if(c){let e=oe(c)?.version;e&&(!n||w(e,n))&&(n=e)}if(s?.dependencies)for(let[e,t]of Object.entries(s.dependencies))typeof t==`string`&&a(e,t)}return a(e,t),n}function d(e,t){for(let[n,r]of Object.entries(e)){let e=u(n,typeof r==`string`?r:r.version);if(e){let r=t?l:c;r[e]??=new Set,r[e].add(n);let i=t?s:a;(!i||w(e,i))&&(t?s=e:a=e)}}}let f=o.relative(t,e)||`.`,p=r.importers[f];p&&(p.dependencies&&d(p.dependencies,!1),p.devDependencies&&d(p.devDependencies,!0));let m=a&&s?w(a,s)?`>=${a}`:`>=${s}`:a?`>=${a}`:s?`>=${s}`:void 0;return{dependencies:a?{topLevelCauses:[...c[a]],version:`>=${a}`}:void 0,devDependencies:s?{topLevelCauses:[...l[s]],version:`>=${s}`}:void 0,lockfile:n,version:m}}function Xe(e){let t=e.devEngines;if(t?.runtime)return(Array.isArray(t.runtime)?t.runtime:[t.runtime]).find(e=>e.name===`node`)?.version}function Ze(e,t){let n=e.devEngines??{};if(n.runtime)if(Array.isArray(n.runtime)){let e=n.runtime.findIndex(e=>e.name===`node`);e===-1?n.runtime.push({name:`node`,version:t}):n.runtime[e].version=t}else n.runtime.name===`node`?n.runtime.version=t:n.runtime=[n.runtime,{name:`node`,version:t}];else n.runtime={name:`node`,version:t};e.devEngines=n}function Qe(e){let t=e.devEngines;t?.runtime&&(Array.isArray(t.runtime)?(t.runtime=t.runtime.filter(e=>e.name!==`node`),t.runtime.length===0?delete t.runtime:t.runtime.length===1&&(t.runtime=t.runtime[0])):t.runtime.name===`node`&&delete t.runtime,Object.keys(t).length===0&&delete e.devEngines)}function Z(e){return e.length===0?``:` (from ${e.join(`, `)})`}async function $e(e,t,n){let i=o.join(n,`package.json`);if(!r.existsSync(i))return 0;let a=r.readJsonSync(i),s=a.engines?.node,c=Xe(a),l=await Ye(n),u=l.dependencies?.version,d=l.dependencies?.topLevelCauses??[],f=l.version,p=l.devDependencies?.topLevelCauses??[],m=[];if(u!==void 0)if(s===void 0){if(m.push(`Missing engines.node — suggest setting to "${u}"${Z(d)}`),t){let e=a.engines??{};e.node=u,a.engines=e}}else{let e=C.minVersion(s),n=C.minVersion(u);e&&n&&C.lt(e,n)&&(m.push(`engines.node is "${s}" but production dependencies require at least "${u}"${Z(d)}`),t&&(a.engines.node=u))}let h=(()=>{if(u&&s){let e=C.minVersion(u),t=C.minVersion(s);if(e&&t)return C.gt(e,t)?u:s}return u??s})(),g=h?C.minVersion(h):void 0,_=f?C.minVersion(f):void 0,ee=_&&g&&C.gt(_,g);if(f!==void 0&&ee)if(c===void 0)m.push(`devDependencies require a higher Node.js minimum (${f}) than engines.node (${h??`none`}) — suggest adding devEngines.runtime for node with version "${f}"${Z(p)}`),t&&Ze(a,f);else{let e=C.minVersion(c);e&&C.lt(e,_)&&(m.push(`devEngines.runtime.version for node is "${c}" but all dependencies require at least "${f}"${Z(p)}`),t&&Ze(a,f))}else if(c!==void 0){let e=C.minVersion(c);e&&g&&C.lte(e,g)&&(m.push(`devEngines.runtime.version for node is redundant (engines.node already covers the requirement) — suggest removing`),t&&Qe(a))}if(m.length>0){e.write(`${t?`Fixed`:`Found`} ${m.length} Node.js version ${P(`issue`,m.length)} in ${i}:\n`);for(let t of m)e.write(` - ${t}\n`);return t?(r.writeJsonSync(i,a,{spaces:` `}),await j(i),0):1}return 0}async function et(e,t){let n=pe(),r=0;for(let i of n)await $e(e,t,i)!==0&&(r=1);return r}async function tt(e){let t=T(await Ye(D())).split(`
`);for(let n of t)e.write(`${n}\n`);return 0}async function nt(e){return et(e,!1)}async function rt(e){return et(e,!0)}const it={commands:{fix:{commands:[{execute:Ke,name:Ge.name},{execute:rt,name:rt.name}],description:`Fix common issues like outdated copyright years in license files. ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},init:{locationOptionFlag:!1},lint:{commands:[{execute:Ge,name:Ke.name},{execute:nt,name:nt.name}],description:`Check the repo for common issues. ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},printConfig:{commands:[{execute:tt,name:tt.name}],description:`Print minimum Node.js version constraints from the pnpm lockfile.`,positionalArgumentMode:`none`}},description:`Kitschpatrol's repository-related shared configuration tools.`,logColor:`gray`,logPrefix:`[Repo Config]`,name:`ksc-repo`,order:1},at=[`--ignore-path`,k(`.gitignore`)??`.gitignore`,`--allow-empty-input`],ot=`**/*.{${[`css`,`scss`,`sass`,`svelte`,`html`,`astro`,`tsx`,`jsx`,`php`,`vue`].join(`,`)}}`;async function st(e,t){let n=`stylelint`,r=await B(n);if(r===void 0)return 1;let{filepath:i,isEmpty:a}=r;if(a)return e.write(`Configuration is empty.
`),0;e.write(`Found ${n} configuration at "${i}"\n`);let s;t.length>0?(s=o.join(process.cwd(),t[0]),e.write(`Showing config for file at "${s}"\n`)):s=A(`package-dir`);let c=T(await ce.resolveConfig(s)).split(`
`);for(let t of c)e.write(`${t}\n`);return 0}const ct={commands:{fix:{commands:[{name:`stylelint`,optionFlags:[...at,`--fix`],receivePositionalArguments:!0}],description:`Fix your project with Stylelint. ${V.fileRun}`,positionalArgumentDefault:ot,positionalArgumentMode:`optional`},init:{configFile:`stylelint.config.js`,configPackageJson:{stylelint:{extends:`@kitschpatrol/stylelint-config`}},locationOptionFlag:!0},lint:{commands:[{name:`stylelint`,optionFlags:at,receivePositionalArguments:!0}],description:`Lint your project with Stylelint. ${V.fileRun}`,positionalArgumentDefault:ot,positionalArgumentMode:`optional`},printConfig:{commands:[{execute:st,name:st.name}],description:`Print the effective Stylelint configuration. ${V.optionalFileRun}.`,positionalArgumentMode:`optional`}},description:`Kitschpatrol's Stylelint shared configuration tools.`,logColor:`greenBright`,logPrefix:`[Stylelint]`,name:`ksc-stylelint`,order:5};async function lt(){let e=D(),t=[`svelte.config.js`,`svelte.config.mjs`,`svelte.config.cjs`].map(async t=>r.exists(o.join(e,t)));return(await Promise.all(t)).some(Boolean)}async function ut(e){return e.write("Skipping `tsc` since this is a Svelte project. Consider running `svelte-check` instead.\n"),0}async function dt(){return await lt()?[{execute:ut,name:ut.name}]:[{cwdOverride:`package-dir`,name:`tsc`,optionFlags:[`--noEmit`]}]}const Q=[Ae,ke,Pe,Ie,Le,Re,it,ct,{commands:{init:{locationOptionFlag:!1},lint:{commands:await dt(),description:`Run type checking on your project. ${V.packageRun} ${V.monorepoRun}`,positionalArgumentMode:`none`},printConfig:{commands:[{name:`tsc`,optionFlags:[`--showConfig`],prettyJsonOutput:!0}],description:`Print the TypeScript configuration for the project. ${V.packageSearch} ${V.monorepoSearch}`,positionalArgumentMode:`none`}},description:`Kitschpatrol's TypeScript shared configuration tools.`,logColor:`blueBright`,logPrefix:`[TypeScript Config]`,name:`ksc-typescript`,order:3}];function $(e,t){t.sort((e,t)=>e.order-t.order);let n=[];for(let r of t)Object.keys(r.commands).includes(e)&&n.push({name:r.name,...e===`init`?{receiveOptionFlags:r.commands[e]?.locationOptionFlag}:{receivePositionalArguments:r.commands[e]?.positionalArgumentMode!==`none`},subcommands:[ve(e)]});return n}await we({commands:{fix:{commands:$(`fix`,Q),description:`Fix your project with multiple tools in one go. ${V.multiArgumentCaveat}`,positionalArgumentMode:`optional`},init:{commands:$(`init`,Q),description:`Initialize configuration files for the entire suite of @kitschpatrol/shared-config tools. ${V.multiOptionCaveat}`,locationOptionFlag:!0},lint:{commands:$(`lint`,Q),description:`Lint your project with multiple tools in one go. ${V.multiArgumentCaveat}`,positionalArgumentMode:`optional`},printConfig:{commands:$(`printConfig`,Q),description:`Print aggregated tool configuration data. ${V.multiArgumentCaveat}`,positionalArgumentMode:`optional`}},description:`Run aggregated @kitschpatrol/shared-config commands.`,logColor:`yellow`,logPrefix:`🔬`,name:`ksc`,order:0,showSummary:!0,verbose:!0});export{};