@bottomlessmargaritas/formatting-configs 1.0.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 +239 -0
- package/configs/eslint.config.js +168 -0
- package/configs/prettier.config.js +50 -0
- package/package.json +48 -0
package/bin/cli.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
copyFileSync,
|
|
6
|
+
mkdirSync,
|
|
7
|
+
readFileSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
readdirSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
} from "node:fs";
|
|
12
|
+
import { dirname, join, resolve } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { execSync } from "node:child_process";
|
|
15
|
+
import { createInterface } from "node:readline";
|
|
16
|
+
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const CONFIGS_DIR = resolve(__dirname, "..", "configs");
|
|
19
|
+
|
|
20
|
+
const isAuto = process.argv.includes("--auto");
|
|
21
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
22
|
+
const skipInstall = process.argv.includes("--skip-install");
|
|
23
|
+
|
|
24
|
+
const PKG_NAME = "@bottomlessmargaritas/formatting-configs";
|
|
25
|
+
|
|
26
|
+
const SCRIPTS_TO_ADD = {
|
|
27
|
+
format: 'prettier --write "**/*.{js,jsx,ts,tsx,json,css,scss,md}"',
|
|
28
|
+
"format:check": 'prettier --check "**/*.{js,jsx,ts,tsx,json,css,scss,md}"',
|
|
29
|
+
lint: "eslint .",
|
|
30
|
+
"lint:fix": "eslint . --fix",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const PEER_DEPS = {
|
|
34
|
+
"@trivago/prettier-plugin-sort-imports": "^4",
|
|
35
|
+
"@typescript-eslint/parser": "^8",
|
|
36
|
+
eslint: "^9.0.0",
|
|
37
|
+
"eslint-config-prettier": "^10",
|
|
38
|
+
"eslint-plugin-security": "^3",
|
|
39
|
+
"eslint-plugin-unused-imports": "^4",
|
|
40
|
+
globals: "^15",
|
|
41
|
+
prettier: "^3",
|
|
42
|
+
"typescript-eslint": "^8",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// React-related deps — only installed if project uses React
|
|
46
|
+
const REACT_PEER_DEPS = {
|
|
47
|
+
"@babel/eslint-parser": "^7",
|
|
48
|
+
"eslint-plugin-jsx-a11y": "^6",
|
|
49
|
+
"eslint-plugin-react": "^7",
|
|
50
|
+
"eslint-plugin-react-hooks": "^5",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
function findProjectRoot(startDir) {
|
|
54
|
+
let dir = startDir;
|
|
55
|
+
while (dir !== dirname(dir)) {
|
|
56
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
57
|
+
return dir;
|
|
58
|
+
}
|
|
59
|
+
dir = dirname(dir);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function getBackupName(filePath) {
|
|
65
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
66
|
+
return `${filePath}.backup-${timestamp}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function detectPackageManager(projectRoot) {
|
|
70
|
+
if (existsSync(join(projectRoot, "pnpm-lock.yaml"))) {
|
|
71
|
+
return "pnpm";
|
|
72
|
+
}
|
|
73
|
+
if (existsSync(join(projectRoot, "yarn.lock"))) {
|
|
74
|
+
return "yarn";
|
|
75
|
+
}
|
|
76
|
+
if (existsSync(join(projectRoot, "bun.lockb")) || existsSync(join(projectRoot, "bun.lock"))) {
|
|
77
|
+
return "bun";
|
|
78
|
+
}
|
|
79
|
+
return "npm";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function projectUsesReact(projectRoot) {
|
|
83
|
+
try {
|
|
84
|
+
const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
|
|
85
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
86
|
+
return "react" in allDeps || "next" in allDeps;
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function prompt(question) {
|
|
93
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
rl.question(question, (answer) => {
|
|
96
|
+
rl.close();
|
|
97
|
+
resolve(answer.trim().toLowerCase());
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function copyConfigFiles(projectRoot) {
|
|
103
|
+
const configFiles = readdirSync(CONFIGS_DIR);
|
|
104
|
+
let copied = 0;
|
|
105
|
+
let backed = 0;
|
|
106
|
+
let skipped = 0;
|
|
107
|
+
|
|
108
|
+
for (const file of configFiles) {
|
|
109
|
+
const source = join(CONFIGS_DIR, file);
|
|
110
|
+
const target = join(projectRoot, file);
|
|
111
|
+
|
|
112
|
+
if (existsSync(target)) {
|
|
113
|
+
const sourceContent = readFileSync(source, "utf8");
|
|
114
|
+
const targetContent = readFileSync(target, "utf8");
|
|
115
|
+
|
|
116
|
+
if (sourceContent === targetContent) {
|
|
117
|
+
skipped++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isAuto) {
|
|
122
|
+
const backupPath = getBackupName(target);
|
|
123
|
+
if (!isDryRun) {
|
|
124
|
+
renameSync(target, backupPath);
|
|
125
|
+
copyFileSync(source, target);
|
|
126
|
+
console.log(` ↪ Backed up existing ${file} → ${backupPath.split("/").pop()}`);
|
|
127
|
+
}
|
|
128
|
+
backed++;
|
|
129
|
+
copied++;
|
|
130
|
+
} else {
|
|
131
|
+
const answer = await prompt(` ${file} already exists and differs. Overwrite? (y/n) `);
|
|
132
|
+
if (answer !== "y") {
|
|
133
|
+
skipped++;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const backupPath = getBackupName(target);
|
|
138
|
+
if (!isDryRun) {
|
|
139
|
+
renameSync(target, backupPath);
|
|
140
|
+
copyFileSync(source, target);
|
|
141
|
+
console.log(` ↪ Backed up → ${backupPath.split("/").pop()}`);
|
|
142
|
+
}
|
|
143
|
+
backed++;
|
|
144
|
+
copied++;
|
|
145
|
+
}
|
|
146
|
+
} else {
|
|
147
|
+
if (!isDryRun) {
|
|
148
|
+
copyFileSync(source, target);
|
|
149
|
+
}
|
|
150
|
+
copied++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log(`${PKG_NAME}: ${copied} config(s) copied, ${backed} backed up, ${skipped} unchanged`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function updatePackageJsonScripts(projectRoot) {
|
|
158
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
159
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
160
|
+
|
|
161
|
+
if (!pkg.scripts) {
|
|
162
|
+
pkg.scripts = {};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let added = 0;
|
|
166
|
+
for (const [name, command] of Object.entries(SCRIPTS_TO_ADD)) {
|
|
167
|
+
if (!pkg.scripts[name]) {
|
|
168
|
+
pkg.scripts[name] = command;
|
|
169
|
+
added++;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (added > 0) {
|
|
174
|
+
if (!isDryRun) {
|
|
175
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 4) + "\n", "utf8");
|
|
176
|
+
}
|
|
177
|
+
console.log(`${PKG_NAME}: Added ${added} script(s) to package.json (format, format:check, lint, lint:fix)`);
|
|
178
|
+
} else {
|
|
179
|
+
console.log(`${PKG_NAME}: All formatting scripts already present in package.json`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function installMissingPeers(projectRoot) {
|
|
184
|
+
const pkgPath = join(projectRoot, "package.json");
|
|
185
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
186
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
187
|
+
|
|
188
|
+
const usesReact = projectUsesReact(projectRoot);
|
|
189
|
+
const requiredDeps = { ...PEER_DEPS, ...(usesReact ? REACT_PEER_DEPS : {}) };
|
|
190
|
+
|
|
191
|
+
const missing = Object.entries(requiredDeps)
|
|
192
|
+
.filter(([name]) => !(name in allDeps))
|
|
193
|
+
.map(([name, version]) => `${name}@${version}`);
|
|
194
|
+
|
|
195
|
+
if (missing.length === 0) {
|
|
196
|
+
console.log(`${PKG_NAME}: All peer dependencies already installed`);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const pm = detectPackageManager(projectRoot);
|
|
201
|
+
const installCmd =
|
|
202
|
+
pm === "yarn"
|
|
203
|
+
? `yarn add --dev ${missing.join(" ")}`
|
|
204
|
+
: `${pm} add -D ${missing.join(" ")}`;
|
|
205
|
+
|
|
206
|
+
console.log(`${PKG_NAME}: Installing ${missing.length} missing peer dep(s) with ${pm}...`);
|
|
207
|
+
|
|
208
|
+
if (!isDryRun) {
|
|
209
|
+
try {
|
|
210
|
+
execSync(installCmd, { cwd: projectRoot, stdio: "inherit" });
|
|
211
|
+
} catch {
|
|
212
|
+
console.warn(`${PKG_NAME}: Peer dependency install failed. Run manually:\n ${installCmd}`);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
console.log(`[dry-run] Would run: ${installCmd}`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function run() {
|
|
220
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
221
|
+
const projectRoot = findProjectRoot(cwd);
|
|
222
|
+
|
|
223
|
+
if (!projectRoot) {
|
|
224
|
+
console.error(`${PKG_NAME}: Could not find project root (no package.json found).`);
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
await copyConfigFiles(projectRoot);
|
|
229
|
+
updatePackageJsonScripts(projectRoot);
|
|
230
|
+
|
|
231
|
+
if (!skipInstall) {
|
|
232
|
+
installMissingPeers(projectRoot);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
run().catch((err) => {
|
|
237
|
+
console.error(`${PKG_NAME}:`, err.message);
|
|
238
|
+
process.exit(1);
|
|
239
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import babelParser from '@babel/eslint-parser';
|
|
2
|
+
import tsEslintParser from '@typescript-eslint/parser';
|
|
3
|
+
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
4
|
+
import jsxA11y from 'eslint-plugin-jsx-a11y';
|
|
5
|
+
import react from 'eslint-plugin-react';
|
|
6
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
7
|
+
import security from 'eslint-plugin-security';
|
|
8
|
+
import unusedImports from 'eslint-plugin-unused-imports';
|
|
9
|
+
import globals from 'globals';
|
|
10
|
+
import tseslint from 'typescript-eslint';
|
|
11
|
+
|
|
12
|
+
export default tseslint.config([
|
|
13
|
+
{
|
|
14
|
+
ignores: ['build', 'dist', 'node_modules', '**/*.d.ts', '.turbo'],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
linterOptions: {
|
|
18
|
+
reportUnusedDisableDirectives: true,
|
|
19
|
+
},
|
|
20
|
+
extends: [security.configs.recommended],
|
|
21
|
+
plugins: {
|
|
22
|
+
security,
|
|
23
|
+
'unused-imports': unusedImports,
|
|
24
|
+
},
|
|
25
|
+
rules: {
|
|
26
|
+
'curly': 'error',
|
|
27
|
+
'no-console': ['warn', { allow: ['warn', 'info', 'error', 'group'] }],
|
|
28
|
+
'no-implicit-globals': 'error',
|
|
29
|
+
'no-param-reassign': ['error', { props: false }],
|
|
30
|
+
'no-shadow': 'warn',
|
|
31
|
+
'no-undef': 'error',
|
|
32
|
+
'no-underscore-dangle': 'off',
|
|
33
|
+
'no-unreachable': 'warn',
|
|
34
|
+
'no-unused-expressions': 'error',
|
|
35
|
+
'no-unused-vars': [
|
|
36
|
+
'warn',
|
|
37
|
+
{
|
|
38
|
+
argsIgnorePattern: '^_',
|
|
39
|
+
varsIgnorePattern: '^_',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
'no-useless-escape': 'off',
|
|
43
|
+
'no-var': 'warn',
|
|
44
|
+
'object-shorthand': ['error', 'always'],
|
|
45
|
+
'prefer-const': 'warn',
|
|
46
|
+
'security/detect-eval-with-expression': 'warn',
|
|
47
|
+
'security/detect-non-literal-fs-filename': 'warn',
|
|
48
|
+
'security/detect-non-literal-regexp': 'warn',
|
|
49
|
+
'security/detect-non-literal-require': 'warn',
|
|
50
|
+
'security/detect-object-injection': 'off',
|
|
51
|
+
'security/detect-possible-timing-attacks': 'warn',
|
|
52
|
+
'unused-imports/no-unused-imports': 'warn',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
files: ['**/*.js', '**/*.mjs', '**/*.cjs'],
|
|
57
|
+
languageOptions: {
|
|
58
|
+
globals: {
|
|
59
|
+
...globals.browser,
|
|
60
|
+
...globals.node,
|
|
61
|
+
},
|
|
62
|
+
parser: babelParser,
|
|
63
|
+
parserOptions: {
|
|
64
|
+
requireConfigFile: false,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
files: ['**/*.ts', '**/*.tsx'],
|
|
70
|
+
extends: [...tseslint.configs.recommended],
|
|
71
|
+
languageOptions: {
|
|
72
|
+
...react.configs.flat.recommended.languageOptions,
|
|
73
|
+
ecmaVersion: 'latest',
|
|
74
|
+
parser: tsEslintParser,
|
|
75
|
+
parserOptions: {
|
|
76
|
+
ecmaFeatures: { jsx: true },
|
|
77
|
+
ecmaVersion: 'latest',
|
|
78
|
+
project: ['./tsconfig.json'],
|
|
79
|
+
sourceType: 'module',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
rules: {
|
|
83
|
+
'@typescript-eslint/ban-ts-comment': ['warn', { 'ts-ignore': 'allow-with-description' }],
|
|
84
|
+
'@typescript-eslint/consistent-type-definitions': 'off',
|
|
85
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
86
|
+
'warn',
|
|
87
|
+
{
|
|
88
|
+
fixStyle: 'inline-type-imports',
|
|
89
|
+
prefer: 'type-imports',
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
'@typescript-eslint/no-empty-object-type': 'warn',
|
|
93
|
+
'@typescript-eslint/no-explicit-any': 'warn',
|
|
94
|
+
'@typescript-eslint/no-floating-promises': 'warn',
|
|
95
|
+
'@typescript-eslint/no-misused-promises': [
|
|
96
|
+
'warn',
|
|
97
|
+
{
|
|
98
|
+
checksVoidReturn: { attributes: false },
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
'@typescript-eslint/no-require-imports': 'warn',
|
|
102
|
+
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
|
103
|
+
'@typescript-eslint/no-unused-expressions': 'warn',
|
|
104
|
+
'@typescript-eslint/no-unused-vars': [
|
|
105
|
+
'warn',
|
|
106
|
+
{
|
|
107
|
+
argsIgnorePattern: '^_',
|
|
108
|
+
varsIgnorePattern: '^_',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
'@typescript-eslint/require-await': 'off',
|
|
112
|
+
'no-undef': 'off',
|
|
113
|
+
'no-var': 'warn',
|
|
114
|
+
'prefer-const': 'warn',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
files: ['**/*.tsx'],
|
|
119
|
+
plugins: {
|
|
120
|
+
'jsx-a11y': jsxA11y,
|
|
121
|
+
react,
|
|
122
|
+
'react-hooks': reactHooks,
|
|
123
|
+
},
|
|
124
|
+
settings: {
|
|
125
|
+
react: { version: 'detect' },
|
|
126
|
+
},
|
|
127
|
+
rules: {
|
|
128
|
+
...reactHooks.configs.recommended.rules,
|
|
129
|
+
...react.configs.flat.recommended.rules,
|
|
130
|
+
...jsxA11y.flatConfigs.recommended.rules,
|
|
131
|
+
'jsx-a11y/anchor-is-valid': 'warn',
|
|
132
|
+
'jsx-a11y/click-events-have-key-events': 'off',
|
|
133
|
+
'jsx-a11y/iframe-has-title': 'off',
|
|
134
|
+
'jsx-a11y/interactive-supports-focus': 'off',
|
|
135
|
+
'jsx-a11y/no-autofocus': 'warn',
|
|
136
|
+
'jsx-a11y/no-noninteractive-element-interactions': 'off',
|
|
137
|
+
'jsx-a11y/no-static-element-interactions': 'off',
|
|
138
|
+
'react-hooks/exhaustive-deps': 'warn',
|
|
139
|
+
'react-hooks/rules-of-hooks': 'warn',
|
|
140
|
+
'react/display-name': 'warn',
|
|
141
|
+
'react/jsx-curly-brace-presence': ['error', 'never'],
|
|
142
|
+
'react/no-unescaped-entities': 'warn',
|
|
143
|
+
'react/prop-types': 'off',
|
|
144
|
+
'react/react-in-jsx-scope': 'off',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
files: [
|
|
149
|
+
'**/__tests__/**',
|
|
150
|
+
'**/__mocks__/**',
|
|
151
|
+
'**/tests/**',
|
|
152
|
+
'**/*.test.ts',
|
|
153
|
+
'**/*.test.tsx',
|
|
154
|
+
],
|
|
155
|
+
languageOptions: {
|
|
156
|
+
globals: {
|
|
157
|
+
...globals.jest,
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
rules: {
|
|
161
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
162
|
+
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
163
|
+
'no-console': 'off',
|
|
164
|
+
'security/detect-non-literal-regexp': 'off',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
eslintConfigPrettier,
|
|
168
|
+
]);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
arrowParens: 'always',
|
|
3
|
+
bracketSpacing: true,
|
|
4
|
+
htmlWhitespaceSensitivity: 'css',
|
|
5
|
+
importOrder: [
|
|
6
|
+
'^react$',
|
|
7
|
+
'^react-dom',
|
|
8
|
+
'.*\\.css$',
|
|
9
|
+
'<THIRD_PARTY_MODULES>',
|
|
10
|
+
'^[../]',
|
|
11
|
+
'^[./]',
|
|
12
|
+
],
|
|
13
|
+
importOrderSeparation: true,
|
|
14
|
+
importOrderSortSpecifiers: true,
|
|
15
|
+
jsxSingleQuote: true,
|
|
16
|
+
overrides: [
|
|
17
|
+
{
|
|
18
|
+
files: ['*.json'],
|
|
19
|
+
options: {
|
|
20
|
+
parser: 'json5',
|
|
21
|
+
quoteProps: 'preserve',
|
|
22
|
+
singleQuote: false,
|
|
23
|
+
trailingComma: 'none',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
files: ['.*rc'],
|
|
28
|
+
options: {
|
|
29
|
+
parser: 'json5',
|
|
30
|
+
quoteProps: 'preserve',
|
|
31
|
+
singleQuote: false,
|
|
32
|
+
trailingComma: 'none',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
files: ['*.yml', '*.yaml'],
|
|
37
|
+
options: {
|
|
38
|
+
singleQuote: true,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
plugins: ['@trivago/prettier-plugin-sort-imports'],
|
|
43
|
+
printWidth: 80,
|
|
44
|
+
quoteProps: 'as-needed',
|
|
45
|
+
semi: true,
|
|
46
|
+
singleQuote: true,
|
|
47
|
+
tabWidth: 2,
|
|
48
|
+
trailingComma: 'all',
|
|
49
|
+
useTabs: false,
|
|
50
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bottomlessmargaritas/formatting-configs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Canonical Prettier and ESLint configs for @nullvoidundefined projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"formatting-configs": "./bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"configs/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"postinstall": "node bin/cli.js --auto"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@babel/eslint-parser": ">=7",
|
|
18
|
+
"@trivago/prettier-plugin-sort-imports": ">=4",
|
|
19
|
+
"@typescript-eslint/parser": ">=8",
|
|
20
|
+
"eslint": ">=9 <10",
|
|
21
|
+
"eslint-config-prettier": ">=10",
|
|
22
|
+
"eslint-plugin-jsx-a11y": ">=6",
|
|
23
|
+
"eslint-plugin-react": ">=7",
|
|
24
|
+
"eslint-plugin-react-hooks": ">=5",
|
|
25
|
+
"eslint-plugin-security": ">=3",
|
|
26
|
+
"eslint-plugin-unused-imports": ">=4",
|
|
27
|
+
"globals": ">=15",
|
|
28
|
+
"prettier": ">=3",
|
|
29
|
+
"typescript-eslint": ">=8"
|
|
30
|
+
},
|
|
31
|
+
"peerDependenciesMeta": {
|
|
32
|
+
"@babel/eslint-parser": { "optional": true },
|
|
33
|
+
"eslint-plugin-jsx-a11y": { "optional": true },
|
|
34
|
+
"eslint-plugin-react": { "optional": true },
|
|
35
|
+
"eslint-plugin-react-hooks": { "optional": true }
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"prettier",
|
|
39
|
+
"eslint",
|
|
40
|
+
"formatting",
|
|
41
|
+
"config"
|
|
42
|
+
],
|
|
43
|
+
"author": "nullvoidundefined",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
}
|
|
48
|
+
}
|