@kitschpatrol/cspell-config
Version:
CSpell configuration for @kitschpatrol/shared-config.
7 lines • 12.7 kB
JavaScript
import"cosmiconfig";import"cosmiconfig-typescript-loader";import{execa as e}from"execa";import t from"fs-extra";import n,{constants as r}from"node:fs";import i from"node:path";import{PassThrough as a,Transform as o}from"node:stream";import{fileURLToPath as s}from"node:url";import{packageUp as c,packageUpSync as l}from"package-up";import u from"picocolors";import d from"yargs";import{hideBin as f}from"yargs/helpers";import p from"@pinojs/json-colorizer";import m from"decircular";import h from"deepmerge";import ee from"json-stringify-pretty-compact";import{findWorkspacesRoot as g}from"find-workspaces";import _,{access as v}from"node:fs/promises";import{stripVTControlCharacters as y}from"node:util";import{getDefaultConfigLoader as b,resolveConfigFileImports as x}from"cspell-lib";import{lint as S}from"cspell";var C=`7.6.2`;function w(e){return e instanceof Error&&`exitCode`in e&&typeof e.exitCode==`number`}function T(e){return p(ee(m(e),{indent:2,replacer(e,t){return typeof t==`function`?t.name:t}}),{colors:{BRACKET:`gray`}})}const te=(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:te}){return h(e,t,n)}function D(){let e=l();if(e===void 0)throw Error(`No package.json found.`);return i.dirname(e)}function O(){let e=g();return e===null?D():i.resolve(e.location)}function k(e){if(e===`workspace-root`)return O();if(e===`package-dir`)return D();if(typeof e==`string`){if(!t.pathExistsSync(e))throw Error(`Custom cwd directory does not exist: ${e}`);return e}return process.cwd()}async function A(e,t){try{let{default:n}=await import(`prettier`),r=await n.resolveConfig(e),i=await n.format(t,{filepath:e,...r});await _.writeFile(e,i,`utf8`)}catch{console.warn(`Skipped formatting ${e} since Prettier is not installed.`)}}async function j(e){try{await A(e,await _.readFile(e,`utf8`))}catch{}}const M=/\r?\n/;function N(e){return new o({transform(t,n,r){let i=t.toString().split(M).filter(t=>t.trim()!==``&&!e(y(t))).join(`
`);this.push(i+`
`),r()}})}function P(e,t){return new o({transform(n,r,i){let a=n.toString().split(M).filter(e=>e.trim().length>0).map(n=>`${e?t===void 0?e:u[t](e):``} ${n}\n`).join(``);this.push(a),i()}})}async function F(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 I(e,t){return t===1?e:e+`s`}async function L(e,t,n,r,i){let a=1,o;if(r.logPrefix===void 0)o=e;else{let t=P(r.logPrefix,r.logColor);t.pipe(e),o=t}i&&o.write(u.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 R(t,n,r,i,o){let s=1,c;if(i.logPrefix===void 0)c=t;else{let e=P(i.logPrefix,i.logColor);e.pipe(t),c=e}let l=i.subcommands??[],u=[...i.receivePositionalArguments?n:[],...i.positionalArguments??[]],d=[...i.receiveOptionFlags?r:[],...i.optionFlags??[]],f=[...l,...d,...u],p=k(i.cwdOverride);o&&c.write(`Running: "${i.name} ${f.join(` `)}"`);let m=i.prettyJsonOutput?new a:c;try{let t=e(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 e=N(i.outputFilter),n=N(i.outputFilter);t.stdout.pipe(e).pipe(m,{end:!1}),t.stderr.pipe(n).pipe(m,{end:!1})}else t.stdout.pipe(m,{end:!1}),t.stderr.pipe(m,{end:!1});if(await t,i.prettyJsonOutput){m.end();let e=await F(m),t=T(JSON.parse(e)).split(`
`);for(let e of t)c.write(`${e}\n`)}s=t.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 z(e){return`execute`in e}const B=/^ksc-/;function V(e){return e.replace(B,``)}function H(e){return e===void 0||e.length===0?[]:e.flatMap(e=>e.split(`,`)).map(e=>V(e.trim()))}function U(e){return e.option(`skip`,{array:!0,describe:`Tool names to skip (with or without "ksc-" prefix).`,type:`string`})}async function W(e,t,n,r,i,a,o){let s=o??[],c=[],l=[];for(let e of r)s.length>0&&s.includes(V(e.name))?l.push(e):c.push(e);if(s.length>0){let t=new Set(l.map(e=>V(e.name))),n=s.filter(e=>!t.has(e));if(n.length>0){let t=r.map(e=>V(e.name)).join(`, `);e.write(`⚠️ ${u.yellow(`Unrecognized --skip ${I(`value`,n.length)}: ${n.join(`, `)}. Available: ${t}`)}\n`)}}let d=[];for(let r of c){let a=await(z(r)?L(e,t,n,r,i):R(e,t,n,r,i));d.push({exitCode:a,name:r.name})}let f=r.length;if(l.length>0){let t=l.map(({name:e})=>e);e.write(`⏭️ ${u.dim(u.bold(`${t.length} / ${f} ${I(`Command`,t.length)} Skipped:`))} ${u.dim(t.join(`, `))}\n`)}if(a){let t=d.filter(({exitCode:e})=>e===0).map(({name:e})=>e),n=d.filter(({exitCode:e})=>e!==0).map(({name:e})=>e);t.length>0&&e.write(`✅ ${u.green(u.bold(`${t.length} / ${f} ${I(`Command`,t.length)} Succeeded:`))} ${u.green(t.join(`, `))}\n`),n.length>0&&e.write(`❌ ${u.red(u.bold(`${n.length} / ${f} ${I(`Command`,n.length)} Failed:`))} ${u.red(n.join(`, `))}\n`)}return+!d.every(({exitCode:e})=>e===0)}async function G(e,r,a,o){let l=await c();if(l===void 0)throw Error("The `init` command must be used in a directory with a package.json file");let u=await c({cwd:s(import.meta.url)});if(u===void 0)return e.write(`Error: The script being called was not in a package, weird.
`),1;let d=i.join(i.dirname(u),`init`),f=i.dirname(l),p=(r===`file`||r===`package`)&&a!==void 0&&o!==void 0;try{if(p){let n=Object.keys(o)[0];if(r===`package`){let r=t.readJsonSync(l);e.write(`Merging: \nPackage config key "${n}" → "${f}" (Because --location is set to "package")\n`);let i=E(r,o);t.writeJSONSync(l,i,{spaces:` `}),await j(l)}else{let r=t.readJsonSync(l);Object.keys(r).includes(n)&&(e.write(`Deleting: \nPackage config key "${n}" in "${f}" (Because --location is set to "file")\n`),delete r[n],t.writeJSONSync(l,r,{spaces:` `}),await j(l))}}if(!await t.pathExists(d))return 0;if((await t.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 t.copy(d,f,{async filter(o,s){let c=n.statSync(o).isFile(),l=n.existsSync(s);if(c){if(p&&r===`package`&&o.includes(a))return l?(e.write(`Deleting: \n"${o}" → "${s}" (Because --location is set to "package")\n`),t.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`))&&i.extname(s)===`.json`){e.write(`Merging: \n"${o}" → "${s}"\n`);let n=t.readJSONSync(o),r=E(t.readJSONSync(s),n);return t.writeJSONSync(s,r,{spaces:` `}),await j(s),!1}return l?(e.write(`Overwriting: \n"${o}" → "${s}"\n`),await j(s),!0):(e.write(`Copying: \n"${o}" → "${s}"\n`),await j(s),!0)}return!0},overwrite:!0})}catch(e){return console.error(String(e)),1}return 0}async function K(e){let{commands:{fix:t,init:n,lint:r,printConfig:i},description:a,logColor:o,logPrefix:s,name:c,showSummary:l,verbose:u}=e,p=P(s,o);p.pipe(process.stdout);let m=d(f(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?U(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=H(e.skip),i=await W(p,[],t===void 0?[]:[`--location`,t],[{async execute(e,t,r){return G(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?U(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=H(e.skip),i=await W(p,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?U(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=H(e.skip),i=await W(p,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?U(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=H(e.skip),a=await W(p,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()}const q={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.`},J=/['\u2019\u2018]s$/;async function Y(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 S(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(J,``)||t===r.href)}}),i}async function X(){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 Z(e,t){let n=await Y(t);if(n.length>0){let t=P(`[Unused Words]`,`cyanBright`);t.pipe(e);for(let e of n)t.write(`${e}\n`);return 1}return 0}async function ne(){let e=import.meta.resolve(`@kitschpatrol/cspell-config`),t=await c({cwd:i.dirname(s(e))});if(t===void 0)throw Error(`Could not find Case Police dictionary parent package.`);let n=i.join(i.dirname(t),`dictionaries`,`case-police.json`);try{await v(n,r.F_OK)}catch{throw Error(`Case Police dictionary file "${n}" does not exist.`)}return n}async function Q(e,t){let n=`[Case Police]`,r=N(e=>{let t=y(e);return!t.startsWith(n)||!t.includes(`→`)});return r.pipe(e),W(r,t,[],[{logColor:`cyanBright`,logPrefix:n,name:`case-police`,optionFlags:[`--dict`,await ne(),`--ignore`,await X()],receivePositionalArguments:!0}])}async function $(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 "${s(t.url)}"\n`);let n=T(await x(t)).split(`
`);for(let t of n)e.write(`${t}\n`);return 0}await K({commands:{init:{configFile:`cspell.config.ts`,configPackageJson:{cspell:{import:`@kitschpatrol/cspell-config`}},locationOptionFlag:!0},lint:{commands:[{name:`cspell`,optionFlags:[`--quiet`],receivePositionalArguments:!0},{execute:Z,name:Z.name},{execute:Q,name:Q.name}],description:`Check for spelling mistakes. ${q.fileRun}`,positionalArgumentDefault:`**/*`,positionalArgumentMode:`optional`},printConfig:{commands:[{execute:$,name:$.name}],description:`Print the resolved CSpell configuration. ${q.packageSearch} ${q.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});export{};