@kitschpatrol/shared-config 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/bin/cli.js +16 -0
  2. package/package.json +25 -13
  3. package/cli.js +0 -174
  4. package/readme.md +0 -95
package/bin/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import F from"chalk";import{execa as O}from"execa";import $ from"meow";import p from"node:path";import{Transform as j}from"node:stream";import{fileURLToPath as A}from"node:url";import{packageUp as h}from"package-up";function S(o,e){return new j({transform(t,n,i){let r=t.toString().split(/\r?\n/).filter(c=>c.trim().length>0).map(c=>`${o?F[e]("["+o+"] "):""}${c}`).join(`
3
+ `)+`
4
+ `;this.push(r),i()}})}function N(o,e){let t=`
5
+ Usage
6
+ $ ${o} [<file|glob> ...]
7
+ `;if(Object.keys(e).length>0){t+=`
8
+ Options`;for(let n of Object.keys(e))switch(n){case"init":{t+=`
9
+ --init, -i Initialize by copying starter config files to your project root.`;break}case"check":{t+=`
10
+ --check, -c Check for and report issues. Same as ${o}.`;break}case"fix":{t+=`
11
+ --fix, -f Fix all auto-fixable issues, and report the un-fixable.`;break}case"printConfig":{t+=`
12
+ --print-config <file> Print the effective configuration for a file`;break}default:console.error(`Unknown command name: ${n}`)}}return t}function T(o){return Object.keys(o).reduce((e,t)=>{let n={};switch(t){case"init":{n={shortFlag:"i",type:"boolean"};break}case"check":{n={aliases:["lint",""],shortFlag:"l",type:"boolean"};break}case"fix":{n={shortFlag:"f",type:"boolean"};break}case"printConfig":{n={type:"boolean"};break}default:console.error(`Unknown command name: ${t}`)}return e[t]=n,e},{})}async function d(o,e,t=[]){if(e.command!==void 0&&typeof e.command=="string"){let n;try{let i=O(e.command,[...e.options??[],...t],{env:{FORCE_COLOR:"true"},stdin:"inherit"});i.stdout?.pipe(o,{end:!1}),await i,n=i.exitCode??1}catch(i){n=typeof i.exitCode=="number"?i.exitCode:1}return n}return console.error(`Invalid optionCommand: ${JSON.stringify(e,void 0,2)}`),1}function C(o,e){o.length===0&&!e.defaultArguments&&(console.error("This command must be used with a file argument"),process.exit(1))}async function w(o,e,t,n){let i=$(N(o,n),{allowUnknownFlags:!1,booleanDefault:void 0,flags:T(n),importMeta:import.meta}),{flags:l,input:r}=i,c=Object.keys(n).reduce((m,a)=>(l[a]&&(m[a]=n[a]),m),{});Object.keys(c).length===0&&(c.check=n.check);let f=S(e,t);f.pipe(process.stdout);let s=0;for(let[m,a]of Object.entries(c))if(typeof a.command=="function"){C(r,a);let g=r.length===0?a.defaultArguments??[]:r,u=a.options??[];s+=await a.command(f,g,u)}else if(typeof a.command=="string")C(r,a),s+=await d(f,a,r.length===0?a.defaultArguments:r);else switch(m){case"init":{let g=await h();if(g===void 0){console.error("The `--init` flag must be used in a directory with a package.json file"),s+=1;break}let u=await h({cwd:A(import.meta.url)});if(u===void 0){console.error("The script being called was not in a package, weird."),s+=1;break}let k=p.join(p.dirname(u),"init/"),y=p.dirname(g);console.log(`Copying initial configuration files from:
13
+ "${k}" \u2192 "${y}"`);let x={command:"cp",options:["-Ri",`${k}`,`${y}`]};s+=await d(f,x);break}case"check":{console.error("No default implementation for check"),s+=1;break}case"fix":{console.error("No default implementation for fix"),s+=1;break}case"printConfig":{console.error("No default implementation for print-config"),s+=1;break}default:{console.error(`Unknown command name: ${m}`),s+=1;break}}process.exit(s>0?1:0)}var b={check:["cspell-config","eslint-config","markdownlint-config","prettier-config","stylelint-config"],init:["cspell-config","eslint-config","markdownlint-config","npm-config","prettier-config","stylelint-config","vscode-config"],printConfig:["cspell-config","eslint-config","npm-config","prettier-config","stylelint-config","vscode-config"],fix:["eslint-config","markdownlint-config","prettier-config","stylelint-config"]};function v(o){return o.replaceAll(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,e=>"-"+e.toLowerCase())}async function R(o,e,t,n){let i=[],l=[];for(let r of e)o.write(`Running "${r} ${n.join(" ")} ${t.join(" ")}"
14
+ `),await d(o,{command:r,options:t},n)===0?i.push(r):l.push(r);return i.length>0&&o.write(`Successful commands: ${i.join(", ")}
15
+ `),l.length>0&&o.write(`Failed commands: ${l.join(", ")}
16
+ `),l.length>0?1:0}await w("shared-config",void 0,"yellow",Object.keys(b).reduce((o,e)=>(o[e]={async command(t,n){return R(t,b[e],[`--${v(e)}`],n)},defaultArguments:[]},o),{}));
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@kitschpatrol/shared-config",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "description": "Linting and formatting for web projects.",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git@github.com:kitschpatrol/shared-config.git"
8
+ "url": "git@github.com:kitschpatrol/shared-config.git",
9
+ "directory": "packages/shared-config"
9
10
  },
10
11
  "bugs": {
11
12
  "url": "https://github.com/kitschpatrol/shared-config/issues",
@@ -22,23 +23,34 @@
22
23
  "pnpm": ">=8.0.0"
23
24
  },
24
25
  "bin": {
25
- "shared-config": "./cli.js"
26
+ "shared-config": "bin/cli.js"
26
27
  },
27
28
  "files": [
28
- "cli.js"
29
+ "bin"
29
30
  ],
30
31
  "dependencies": {
31
- "minimist": "^1.2.8",
32
- "zx": "^7.2.3",
33
- "@kitschpatrol/markdownlint-config": "2.0.0",
34
- "@kitschpatrol/cspell-config": "2.0.0",
35
- "@kitschpatrol/npm-config": "2.0.0",
36
- "@kitschpatrol/stylelint-config": "2.0.0",
37
- "@kitschpatrol/vscode-config": "2.0.0",
38
- "@kitschpatrol/prettier-config": "2.0.0",
39
- "@kitschpatrol/eslint-config": "2.0.0"
32
+ "chalk": "^5.3.0",
33
+ "execa": "^8.0.1",
34
+ "meow": "^12.1.1",
35
+ "package-up": "^5.0.0",
36
+ "@kitschpatrol/eslint-config": "2.1.0",
37
+ "@kitschpatrol/npm-config": "2.1.0",
38
+ "@kitschpatrol/cspell-config": "2.1.0",
39
+ "@kitschpatrol/markdownlint-config": "2.1.0",
40
+ "@kitschpatrol/prettier-config": "2.1.0",
41
+ "@kitschpatrol/stylelint-config": "2.1.0",
42
+ "@kitschpatrol/vscode-config": "2.1.0"
43
+ },
44
+ "devDependencies": {
45
+ "camelcase": "^8.0.0",
46
+ "globby": "^14.0.0"
40
47
  },
41
48
  "publishConfig": {
42
49
  "access": "public"
50
+ },
51
+ "scripts": {
52
+ "build": "./scripts/capability-generator.ts && ../../scripts/build.ts",
53
+ "clean": "pnpm run bin-restore && git clean -fdX",
54
+ "cli": "node ./bin/cli.js"
43
55
  }
44
56
  }
package/cli.js DELETED
@@ -1,174 +0,0 @@
1
- #!/usr/bin/env zx
2
- /* eslint-disable unicorn/prefer-module */
3
- import minimist from 'minimist';
4
- import { spawn } from 'node:child_process';
5
- import { once } from 'node:events';
6
- import { $ } from 'zx';
7
-
8
- const options = ['fix', 'init', 'check'];
9
- async function main() {
10
- const args = minimist(process.argv.slice(2), {
11
- boolean: options,
12
- });
13
-
14
- const argumentCount = options.reduce((count, argument) => count + (args[argument] ? 1 : 0), 0);
15
- if (argumentCount !== 1) {
16
- console.error(`[SharedConfig] Please provide exactly one of ${options.map((opt) => `--${opt}`).join(' or ')}`);
17
- process.exit(1);
18
- }
19
-
20
- const commands = [
21
- 'cspell-config',
22
- 'eslint-config',
23
- 'markdownlint-config',
24
- 'npm-config',
25
- 'prettier-config',
26
- 'stylelint-config',
27
- 'vscode-config',
28
- ];
29
-
30
- const capabilities = await getCapabilities(commands);
31
-
32
- process.env.FORCE_COLOR = '1';
33
- if (args.init) {
34
- const capabilitiesWithInit = stylePrefixes(capabilities.filter((capability) => capability.init));
35
-
36
- for (const capability of capabilitiesWithInit) {
37
- console.log(`${capability.name} Running command: ${capability.command} --init`);
38
- await $`${capability.command} --init`;
39
- }
40
- console.log('Initialization complete.');
41
- } else if (args.fix) {
42
- runAllCommands(capabilities, 'fix');
43
- } else if (args.check) {
44
- runAllCommands(capabilities, 'check');
45
- }
46
- }
47
-
48
- await main();
49
-
50
- // helpers
51
- async function runCommand(command, prefix) {
52
- // console.log(`${prefix} Running command: ${command}`);
53
-
54
- const subprocess = spawn(command, {
55
- env: { ...process.env, FORCE_COLOR: true },
56
- shell: true,
57
- });
58
-
59
- subprocess.stdout.on('data', (data) => {
60
- const lines = data.toString().split(/\r?\n/);
61
- const prefixedLines = lines
62
- .filter((line) => line.trim() !== '') // Filter out empty lines
63
- .map((line) => `${prefix} ${line}`)
64
- .join('\n');
65
- if (prefixedLines) {
66
- process.stdout.write(prefixedLines); // Add a newline at the end if there's output
67
- }
68
- });
69
-
70
- subprocess.stderr.on('data', (data) => {
71
- const lines = data.toString().split(/\r?\n/);
72
- const prefixedLines = lines
73
- .filter((line) => line.trim() !== '') // Filter out empty lines
74
- .map((line) => `${prefix} ${line}`)
75
- .join('\n');
76
- if (prefixedLines) {
77
- console.error(prefixedLines); // Add a newline at the end if there's output
78
- }
79
- });
80
-
81
- // Wait for the 'close' event
82
- const [code] = await once(subprocess, 'close');
83
- if (code !== 0) {
84
- throw new Error(`${prefix} Command failed with exit code ${code}`);
85
- }
86
- }
87
-
88
- function stylePrefixes(capabilities) {
89
- // ANSI color codes (excluding red and ANSI 256-colors)
90
- const colors = [
91
- '\u001B[32m', // Green
92
- '\u001B[34m', // Blue
93
- '\u001B[33m', // Yellow
94
- '\u001B[35m', // Magenta
95
- '\u001B[36m', // Cyan
96
- '\u001B[94m', // Bright Blue
97
- '\u001B[92m', // Bright Green
98
- '\u001B[93m', // Bright Yellow
99
- '\u001B[95m', // Bright Magenta
100
- ];
101
- const boldCode = '\u001B[1m';
102
- const resetCode = '\u001B[0m';
103
-
104
- // Find the length of the longest prefix
105
- const longestPrefixLength = capabilities.reduce((max, capability) => Math.max(max, capability.name.length), 0);
106
-
107
- // Pad each prefix with spaces to match the length of the longest prefix
108
- return capabilities.map((cmd, index) => {
109
- const colorCode = boldCode + colors[index % colors.length];
110
- const paddedLength = longestPrefixLength;
111
- const paddingLength = paddedLength - cmd.name.length;
112
- const paddedPrefix = colorCode + '[' + cmd.name + ' '.repeat(paddingLength) + ']' + resetCode;
113
- return { ...cmd, name: paddedPrefix };
114
- });
115
- }
116
-
117
- export async function runAllCommands(capabilities, option) {
118
- const capabilitiesWithCommand = stylePrefixes(capabilities.filter((commandInfo) => commandInfo[option]));
119
-
120
- let errors = [];
121
- for (const cmd of capabilitiesWithCommand) {
122
- try {
123
- await runCommand(`${cmd.command} --${option}`, cmd.name);
124
- } catch (error) {
125
- errors.push(error.message);
126
- }
127
- }
128
-
129
- if (errors.length > 0) {
130
- console.error(`${errors.length} task(s) failed:`);
131
- for (const error of errors) console.error(error);
132
- process.exit(1);
133
- } else {
134
- console.log('All tasks completed successfully.');
135
- process.exit(0);
136
- }
137
- }
138
-
139
- async function getCapabilities(commands) {
140
- $.verbose = false;
141
- const capabilities = await Promise.all(
142
- commands.map(async (command) => {
143
- try {
144
- await $`${command}`;
145
- // console.log('Command completed successfully.');
146
- } catch (error) {
147
- return {
148
- command,
149
- ...parseCapabilities(error.stderr.trim()),
150
- };
151
- }
152
-
153
- console.error(`[SharedConfig] ${command} did not return any capabilities.`);
154
- return '';
155
- }),
156
- );
157
- $.verbose = true;
158
- return capabilities;
159
- }
160
-
161
- function parseCapabilities(output) {
162
- const parts = output.match(/\[(.*?)] Please provide exactly one of (.*)/);
163
- if (!parts) return;
164
-
165
- const name = parts[1];
166
- const options = new Set(parts[2].split(' or ').map((opt) => opt.trim().replace('--', '')));
167
-
168
- return {
169
- check: options.has('check'),
170
- fix: options.has('fix'),
171
- init: options.has('init'),
172
- name,
173
- };
174
- }
package/readme.md DELETED
@@ -1,95 +0,0 @@
1
- # shared-config
2
-
3
- ## Overview
4
-
5
- A collection of shared configurations for various linters and tools.
6
-
7
- This package takes a maximalist approach, bundling plugins I need on a regular basis into a single dependency.
8
-
9
- It takes care of dependencies and configurations for:
10
-
11
- - CSpell
12
- - ESLint (including Svelte, Astro, and Typescript support)
13
- - Stylelint
14
- - markdownlint
15
- - NPM (`.npmrc` config)
16
- - Prettier (including a bunch of extra plugins)
17
- - VSCode (extension recommendations and extension settings)
18
-
19
- It's only been tested with `pnpm`.
20
-
21
- ## Setup
22
-
23
- 1. Install the requisite `.npmrc`:
24
-
25
- ```sh
26
- pnpm dlx @kitschpatrol/npm-config
27
- ```
28
-
29
- 2. Install the package:
30
-
31
- ```sh
32
- pnpm add -D @kitschpatrol/shared-config
33
- ```
34
-
35
- 3. Add default config files for all the tools to your project root:
36
-
37
- ```sh
38
- pnpm shared-config --init
39
- ```
40
-
41
- 4. Add helper scripts to your `package.json`:
42
-
43
- These work a bit like [npm-run-all](https://github.com/mysticatea/npm-run-all) to invoke all of the bundled tools.
44
-
45
- ```json
46
- ...
47
- "scripts": {
48
- "format": "shared-config --fix",
49
- "lint": "shared-config --lint",
50
- }
51
- ...
52
- ```
53
-
54
- Note that Prettier formatting for Ruby requires some extra legwork to configure, see [`@kitschpatrol/prettier-config`](https://github.com/kitschpatrol/prettier-config) for more details.
55
-
56
- ## Usage
57
-
58
- Various VSCode plugins should "just work".
59
-
60
- To lint your entire project, after configuring the `package.json` as shown above:
61
-
62
- ```sh
63
- pnpm run lint
64
- ```
65
-
66
- To run all of the tools in a _potentially destructive_ "fix" capacity:
67
-
68
- ```sh
69
- pnpm run format
70
- ```
71
-
72
- ## Todo
73
-
74
- - [ ] `.tsconfig`?
75
- - [ ] Interactive override / merge prompt
76
- - [ ] DRY script invocation / initial config copying?
77
-
78
- ## Issues
79
-
80
- - CSpell, markdownlint, ESLint, and Prettier all need to be hoisted via `public-hoist-pattern` to be accessible in `pnpm exec` scripts and to VSCode plugins.
81
-
82
- - Even basic file-only packages like `vscode-config` and `npm-config` seem to need to be hoisted via for their bin scripts to be accessible via `pnpm exec`
83
-
84
- ## Dev Notes
85
-
86
- - Note that `prettier` and `eslint` packages are [hoisted by default](https://pnpm.io/npmrc#public-hoist-pattern) in `pnpm`
87
-
88
- - For local development via `pnpm`, use `file:` dependency protocol instead of `link:`
89
-
90
- ## Reference projects
91
-
92
- - [1stG/configs](https://github.com/1stG/configs)
93
- - [sheriff](https://www.eslint-config-sheriff.dev)
94
- - [xo](https://github.com/xojs/xo)
95
- - [standard](https://standardjs.com)