@kitschpatrol/shared-config 2.0.1 → 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.
- package/bin/cli.js +16 -0
- package/package.json +25 -13
- package/cli.js +0 -175
- 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
|
|
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": "
|
|
26
|
+
"shared-config": "bin/cli.js"
|
|
26
27
|
},
|
|
27
28
|
"files": [
|
|
28
|
-
"
|
|
29
|
+
"bin"
|
|
29
30
|
],
|
|
30
31
|
"dependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"@kitschpatrol/
|
|
36
|
-
"@kitschpatrol/npm-config": "2.
|
|
37
|
-
"@kitschpatrol/
|
|
38
|
-
"@kitschpatrol/
|
|
39
|
-
"@kitschpatrol/prettier-config": "2.
|
|
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,175 +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
|
-
console.log('\n');
|
|
40
|
-
}
|
|
41
|
-
console.log('Initialization complete.');
|
|
42
|
-
} else if (args.fix) {
|
|
43
|
-
runAllCommands(capabilities, 'fix');
|
|
44
|
-
} else if (args.check) {
|
|
45
|
-
runAllCommands(capabilities, 'check');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
await main();
|
|
50
|
-
|
|
51
|
-
// helpers
|
|
52
|
-
async function runCommand(command, prefix) {
|
|
53
|
-
// console.log(`${prefix} Running command: ${command}`);
|
|
54
|
-
|
|
55
|
-
const subprocess = spawn(command, {
|
|
56
|
-
env: { ...process.env, FORCE_COLOR: true },
|
|
57
|
-
shell: true,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
subprocess.stdout.on('data', (data) => {
|
|
61
|
-
const lines = data.toString().split(/\r?\n/);
|
|
62
|
-
const prefixedLines = lines
|
|
63
|
-
.filter((line) => line.trim() !== '') // Filter out empty lines
|
|
64
|
-
.map((line) => `${prefix} ${line}`)
|
|
65
|
-
.join('\n');
|
|
66
|
-
if (prefixedLines) {
|
|
67
|
-
process.stdout.write(prefixedLines); // Add a newline at the end if there's output
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
subprocess.stderr.on('data', (data) => {
|
|
72
|
-
const lines = data.toString().split(/\r?\n/);
|
|
73
|
-
const prefixedLines = lines
|
|
74
|
-
.filter((line) => line.trim() !== '') // Filter out empty lines
|
|
75
|
-
.map((line) => `${prefix} ${line}`)
|
|
76
|
-
.join('\n');
|
|
77
|
-
if (prefixedLines) {
|
|
78
|
-
console.error(prefixedLines); // Add a newline at the end if there's output
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Wait for the 'close' event
|
|
83
|
-
const [code] = await once(subprocess, 'close');
|
|
84
|
-
if (code !== 0) {
|
|
85
|
-
throw new Error(`${prefix} Command failed with exit code ${code}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function stylePrefixes(capabilities) {
|
|
90
|
-
// ANSI color codes (excluding red and ANSI 256-colors)
|
|
91
|
-
const colors = [
|
|
92
|
-
'\u001B[32m', // Green
|
|
93
|
-
'\u001B[34m', // Blue
|
|
94
|
-
'\u001B[33m', // Yellow
|
|
95
|
-
'\u001B[35m', // Magenta
|
|
96
|
-
'\u001B[36m', // Cyan
|
|
97
|
-
'\u001B[94m', // Bright Blue
|
|
98
|
-
'\u001B[92m', // Bright Green
|
|
99
|
-
'\u001B[93m', // Bright Yellow
|
|
100
|
-
'\u001B[95m', // Bright Magenta
|
|
101
|
-
];
|
|
102
|
-
const boldCode = '\u001B[1m';
|
|
103
|
-
const resetCode = '\u001B[0m';
|
|
104
|
-
|
|
105
|
-
// Find the length of the longest prefix
|
|
106
|
-
const longestPrefixLength = capabilities.reduce((max, capability) => Math.max(max, capability.name.length), 0);
|
|
107
|
-
|
|
108
|
-
// Pad each prefix with spaces to match the length of the longest prefix
|
|
109
|
-
return capabilities.map((cmd, index) => {
|
|
110
|
-
const colorCode = boldCode + colors[index % colors.length];
|
|
111
|
-
const paddedLength = longestPrefixLength;
|
|
112
|
-
const paddingLength = paddedLength - cmd.name.length;
|
|
113
|
-
const paddedPrefix = colorCode + '[' + cmd.name + ' '.repeat(paddingLength) + ']' + resetCode;
|
|
114
|
-
return { ...cmd, name: paddedPrefix };
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export async function runAllCommands(capabilities, option) {
|
|
119
|
-
const capabilitiesWithCommand = stylePrefixes(capabilities.filter((commandInfo) => commandInfo[option]));
|
|
120
|
-
|
|
121
|
-
let errors = [];
|
|
122
|
-
for (const cmd of capabilitiesWithCommand) {
|
|
123
|
-
try {
|
|
124
|
-
await runCommand(`${cmd.command} --${option}`, cmd.name);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
errors.push(error.message);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (errors.length > 0) {
|
|
131
|
-
console.error(`${errors.length} task(s) failed:`);
|
|
132
|
-
for (const error of errors) console.error(error);
|
|
133
|
-
process.exit(1);
|
|
134
|
-
} else {
|
|
135
|
-
console.log('All tasks completed successfully.');
|
|
136
|
-
process.exit(0);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
async function getCapabilities(commands) {
|
|
141
|
-
$.verbose = false;
|
|
142
|
-
const capabilities = await Promise.all(
|
|
143
|
-
commands.map(async (command) => {
|
|
144
|
-
try {
|
|
145
|
-
await $`${command}`;
|
|
146
|
-
// console.log('Command completed successfully.');
|
|
147
|
-
} catch (error) {
|
|
148
|
-
return {
|
|
149
|
-
command,
|
|
150
|
-
...parseCapabilities(error.stderr.trim()),
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
console.error(`[SharedConfig] ${command} did not return any capabilities.`);
|
|
155
|
-
return '';
|
|
156
|
-
}),
|
|
157
|
-
);
|
|
158
|
-
$.verbose = true;
|
|
159
|
-
return capabilities;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function parseCapabilities(output) {
|
|
163
|
-
const parts = output.match(/\[(.*?)] Please provide exactly one of (.*)/);
|
|
164
|
-
if (!parts) return;
|
|
165
|
-
|
|
166
|
-
const name = parts[1];
|
|
167
|
-
const options = new Set(parts[2].split(' or ').map((opt) => opt.trim().replace('--', '')));
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
check: options.has('check'),
|
|
171
|
-
fix: options.has('fix'),
|
|
172
|
-
init: options.has('init'),
|
|
173
|
-
name,
|
|
174
|
-
};
|
|
175
|
-
}
|
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)
|