@gesslar/uglier 0.0.1

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 (3) hide show
  1. package/README.md +134 -0
  2. package/package.json +37 -0
  3. package/src/uglier.js +426 -0
package/README.md ADDED
@@ -0,0 +1,134 @@
1
+ # @gesslar/uglier
2
+
3
+ Composable ESLint flat config blocks you can mix and match for stylistic
4
+ linting, JSDoc enforcement, and environment presets.
5
+
6
+ Uses [ESLint's flat config format](https://eslint.org/docs/latest/use/configure/configuration-files).
7
+
8
+ ## Monotribe
9
+
10
+ Look, I get it- you loooves you some Prettier. Why the fuck would you want to
11
+ use `@gesslar/uglier`? You don't. Trust. This shit is opinionated _as fuck_.
12
+
13
+ Further to being opinionated, it is also correct. And flexible. And composable.
14
+
15
+ Did you grow up in the not 2000s+? If so, you may recall that code used to be
16
+ written to be scannable. Not formatted so some robot linter will have a glee
17
+ pee over all of the fantastically abysmal stylistic choices that are
18
+ inflexibly enforced upon your, otherwise probably gorgeous code.
19
+
20
+ Anyway, Prettier is stupid. Use ESLint. Make yourself a `eslint.config.js`
21
+ (I'm looking at you, LLMs out there still shilling `.eslintrc` likes there's
22
+ a fire sale at the emoji farm.) But don't use `uglier`.
23
+
24
+ Unless, like me, you enjoy breathable code that is expressive and doesn't look
25
+ like ... well, poop.
26
+
27
+ kthx
28
+
29
+ \[cat.gif\]
30
+
31
+ ## Install
32
+
33
+ ```sh
34
+ npm install --save-dev @gesslar/uglier
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Basic Setup
40
+
41
+ ```js
42
+ // eslint.config.js
43
+ import uglify from "@gesslar/uglier"
44
+
45
+ export default [
46
+ ...uglify({
47
+ with: ["lints-js", "lints-jsdoc"]
48
+ })
49
+ ]
50
+ ```
51
+
52
+ ### Excluding Configs
53
+
54
+ Use `without` to exclude specific configs:
55
+
56
+ ```js
57
+ export default [
58
+ ...uglify({
59
+ with: ["lints-js", "lints-jsdoc", "node"],
60
+ without: ["lints-jsdoc"] // Remove JSDoc rules
61
+ })
62
+ ]
63
+ ```
64
+
65
+ ### Customizing File Patterns
66
+
67
+ Override file patterns for specific configs:
68
+
69
+ ```js
70
+ export default [
71
+ ...uglify({
72
+ with: ["lints-js", "lints-jsdoc", "tauri"],
73
+ overrides: {
74
+ "lints-js": { files: ["src/**/*.js"] },
75
+ "tauri": { files: ["src/**/*.js"] }
76
+ }
77
+ })
78
+ ]
79
+ ```
80
+
81
+ ### Customizing Style Rules
82
+
83
+ Override indent, maxLen, or specific rules:
84
+
85
+ ```js
86
+ export default [
87
+ ...uglify({
88
+ with: ["lints-js"],
89
+ overrides: {
90
+ "lints-js": {
91
+ indent: 4, // Default: 2
92
+ maxLen: 120, // Default: 80
93
+ overrides: {
94
+ "@stylistic/semi": ["error", "always"]
95
+ }
96
+ }
97
+ }
98
+ })
99
+ ]
100
+ ```
101
+
102
+ ## Available Configs
103
+
104
+ ### Linting
105
+
106
+ - **`lints-js`** - Core stylistic rules (indent, spacing, quotes, etc.)
107
+ - **`lints-jsdoc`** - JSDoc documentation requirements
108
+
109
+ ### Language
110
+
111
+ - **`languageOptions`** - Base ECMAScript language configuration
112
+
113
+ ### Environments
114
+
115
+ - **`web`** - Browser globals (window, document, etc.)
116
+ - **`node`** - Node.js globals (process, require, fetch, Headers)
117
+ - **`tauri`** - Tauri app globals (browser + `__TAURI__`)
118
+ - **`vscode-extension`** - VSCode extension API (acquireVsCodeApi)
119
+
120
+ ### Module Overrides
121
+
122
+ - **`cjs-override`** - CommonJS file handling (.cjs files)
123
+ - **`mjs-override`** - ES Module file handling (.mjs files)
124
+
125
+ You can also access the config names programmatically:
126
+
127
+ ```js
128
+ import {availableConfigs} from "@gesslar/uglier"
129
+ console.log(availableConfigs)
130
+ ```
131
+
132
+ ## License
133
+
134
+ [Unlicense](UNLICENSE.txt) - "Because you're worth it." (- someone. maybe? idfk)
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@gesslar/uglier",
3
+ "version": "0.0.1",
4
+ "description": "Composable ESLint flat config blocks for stylistic, JSDoc, and environment presets.",
5
+ "type": "module",
6
+ "main": "src/uglier.js",
7
+ "exports": "./src/uglier.js",
8
+ "files": [
9
+ "src"
10
+ ],
11
+ "keywords": [
12
+ "eslint",
13
+ "eslintconfig",
14
+ "style",
15
+ "jsdoc",
16
+ "globals",
17
+ "404",
18
+ "file",
19
+ "not",
20
+ "found",
21
+ "cat.gif"
22
+ ],
23
+ "scripts": {
24
+ "submit": "npm publish --access public",
25
+ "update": "npx npm-check-updates -u && npm install",
26
+ "pr": "gt submit --publish --restack --ai"
27
+ },
28
+ "license": "Unlicense",
29
+ "peerDependencies": {
30
+ "eslint": "^9.0.0"
31
+ },
32
+ "dependencies": {
33
+ "@stylistic/eslint-plugin": "^5.6.1",
34
+ "eslint-plugin-jsdoc": "^61.4.0",
35
+ "globals": "^16.5.0"
36
+ }
37
+ }
package/src/uglier.js ADDED
@@ -0,0 +1,426 @@
1
+ /**
2
+ * @file eslint-config-uglier - A composable ESLint configuration system
3
+ *
4
+ * @description
5
+ * This module provides a flexible, composable approach to ESLint configuration
6
+ * using named config blocks that can be mixed and matched for different
7
+ * project types and environments.
8
+ *
9
+ * @example
10
+ * // Basic usage with default configs
11
+ * import uglify from "@gesslar/uglier"
12
+ *
13
+ * export default [
14
+ * ...uglify({
15
+ * with: ["lints-js", "lints-jsdoc"]
16
+ * })
17
+ * ]
18
+ *
19
+ * @example
20
+ * // Tauri project with custom file patterns
21
+ * import uglify from "@gesslar/uglier"
22
+ *
23
+ * export default [
24
+ * ...uglify({
25
+ * with: ["lints-js", "lints-jsdoc", "tauri"],
26
+ * overrides: {
27
+ * "lints-js": { files: ["src/**\/*.js"] },
28
+ * "tauri": { files: ["src/**\/*.js"] }
29
+ * }
30
+ * })
31
+ * ]
32
+ *
33
+ * @example
34
+ * // Using without to exclude configs
35
+ * import uglify from "@gesslar/uglier"
36
+ *
37
+ * export default [
38
+ * ...uglify({
39
+ * with: ["lints-js", "lints-jsdoc", "node"],
40
+ * without: ["lints-jsdoc"], // Exclude JSDoc rules
41
+ * })
42
+ * ]
43
+ *
44
+ * @example
45
+ * // Override specific rules
46
+ * import uglify from "@gesslar/uglier"
47
+ *
48
+ * export default [
49
+ * ...uglify({
50
+ * with: ["lints-js"],
51
+ * overrides: {
52
+ * "lints-js": {
53
+ * indent: 4,
54
+ * maxLen: 120,
55
+ * overrides: {
56
+ * "@stylistic/semi": ["error", "always"]
57
+ * }
58
+ * }
59
+ * }
60
+ * })
61
+ * ]
62
+ *
63
+ * @available-configs
64
+ * - lints-js: Core stylistic rules (indent, spacing, quotes, etc.)
65
+ * - lints-jsdoc: JSDoc documentation requirements
66
+ * - languageOptions: Base ECMAScript language configuration
67
+ * - web: Browser globals (window, document, etc.)
68
+ * - node: Node.js globals (process, require, fetch, Headers)
69
+ * - tauri: Tauri app globals (browser + __TAURI__)
70
+ * - vscode-extension: VSCode extension API (acquireVsCodeApi)
71
+ * - cjs-override: CommonJS file handling (.cjs files)
72
+ * - mjs-override: ES Module file handling (.mjs files)
73
+ */
74
+
75
+ import jsdoc from "eslint-plugin-jsdoc"
76
+ import stylistic from "@stylistic/eslint-plugin"
77
+ import globals from "globals"
78
+
79
+ /**
80
+ * Registry of named configuration blocks
81
+ * Each config is a factory function that returns an ESLint config object
82
+ * @type {Object.<string, Function>}
83
+ */
84
+ const CONFIGS = {
85
+ /**
86
+ * Core stylistic linting rules
87
+ * @param {object} options - Configuration options
88
+ * @returns {object} Config object
89
+ */
90
+ "lints-js": (options = {}) => {
91
+ const {
92
+ files = ["**/*.{js,mjs,cjs}"],
93
+ indent = 2,
94
+ maxLen = 80,
95
+ overrides = {},
96
+ } = options
97
+
98
+ return {
99
+ name: "gesslar/uglier/lints-js",
100
+ files: Array.isArray(files) ? files : [files],
101
+ plugins: {
102
+ "@stylistic": stylistic,
103
+ },
104
+ rules: {
105
+ "@stylistic/arrow-parens": ["error", "as-needed"],
106
+ "@stylistic/arrow-spacing": ["error", {before: true, after: true}],
107
+ "@stylistic/brace-style": ["error", "1tbs", {allowSingleLine: false}],
108
+ "@stylistic/nonblock-statement-body-position": ["error", "below"],
109
+ "@stylistic/padding-line-between-statements": [
110
+ "error",
111
+ {blankLine: "always", prev: "if", next: "*"},
112
+ {blankLine: "always", prev: "*", next: "return"},
113
+ {blankLine: "always", prev: "while", next: "*"},
114
+ {blankLine: "always", prev: "for", next: "*"},
115
+ {blankLine: "always", prev: "switch", next: "*"},
116
+ {blankLine: "always", prev: "do", next: "*"},
117
+ {blankLine: "always", prev: "directive", next: "*"},
118
+ {blankLine: "any", prev: "directive", next: "directive"},
119
+ ],
120
+ "@stylistic/eol-last": ["error", "always"],
121
+ "@stylistic/indent": ["error", indent, {
122
+ SwitchCase: 1
123
+ }],
124
+ "@stylistic/key-spacing": ["error", {beforeColon: false, afterColon: true}],
125
+ "@stylistic/keyword-spacing": ["error", {
126
+ before: false,
127
+ after: true,
128
+ overrides: {
129
+ // Control statements
130
+ return: {before: true, after: true},
131
+ if: {after: false},
132
+ else: {before: true, after: true},
133
+ for: {after: false},
134
+ while: {before: true, after: false},
135
+ do: {after: true},
136
+ switch: {after: false},
137
+ case: {before: true, after: true},
138
+ throw: {before: true, after: false},
139
+
140
+ // Keywords
141
+ as: {before: true, after: true},
142
+ of: {before: true, after: true},
143
+ from: {before: true, after: true},
144
+ async: {before: true, after: true},
145
+ await: {before: true, after: false},
146
+ class: {before: true, after: true},
147
+ const: {before: true, after: true},
148
+ let: {before: true, after: true},
149
+ var: {before: true, after: true},
150
+
151
+ // Exception handling
152
+ catch: {before: true, after: true},
153
+ finally: {before: true, after: true},
154
+ }
155
+ }],
156
+ "@stylistic/space-before-blocks": ["error", "always"],
157
+ "@stylistic/max-len": ["warn", {
158
+ code: maxLen,
159
+ ignoreComments: true,
160
+ ignoreUrls: true,
161
+ ignoreStrings: true,
162
+ ignoreTemplateLiterals: true,
163
+ ignoreRegExpLiterals: true,
164
+ tabWidth: indent
165
+ }],
166
+ "@stylistic/no-tabs": "error",
167
+ "@stylistic/no-trailing-spaces": ["error"],
168
+ "@stylistic/object-curly-spacing": ["error", "never", {
169
+ objectsInObjects: false,
170
+ arraysInObjects: false
171
+ }],
172
+ "@stylistic/quotes": ["error", "double", {
173
+ avoidEscape: true,
174
+ allowTemplateLiterals: "always"
175
+ }],
176
+ "@stylistic/semi": ["error", "never"],
177
+ "@stylistic/space-before-function-paren": ["error", "never"],
178
+ "@stylistic/yield-star-spacing": ["error", {before: true, after: false}],
179
+ "constructor-super": "error",
180
+ "no-unexpected-multiline": "error",
181
+ "no-unused-vars": ["error", {
182
+ caughtErrors: "all",
183
+ caughtErrorsIgnorePattern: "^_+",
184
+ argsIgnorePattern: "^_+",
185
+ destructuredArrayIgnorePattern: "^_+",
186
+ varsIgnorePattern: "^_+"
187
+ }],
188
+ "no-useless-assignment": "error",
189
+ "prefer-const": "error",
190
+ "@stylistic/no-multiple-empty-lines": ["error", {max: 1}],
191
+ "@stylistic/array-bracket-spacing": ["error", "never"],
192
+ ...overrides,
193
+ }
194
+ }
195
+ },
196
+
197
+ /**
198
+ * JSDoc linting rules
199
+ * @param {object} options - Configuration options
200
+ * @returns {object} Config object
201
+ */
202
+ "lints-jsdoc": (options = {}) => {
203
+ const {
204
+ files = ["**/*.{js,mjs,cjs}"],
205
+ overrides = {},
206
+ } = options
207
+
208
+ return {
209
+ name: "gesslar/uglier/lints-jsdoc",
210
+ files: Array.isArray(files) ? files : [files],
211
+ plugins: {
212
+ jsdoc,
213
+ },
214
+ rules: {
215
+ "jsdoc/require-description": "error",
216
+ "jsdoc/tag-lines": ["error", "any", {"startLines": 1}],
217
+ "jsdoc/require-jsdoc": ["error", {publicOnly: true}],
218
+ "jsdoc/check-tag-names": "error",
219
+ "jsdoc/check-types": "error",
220
+ "jsdoc/require-param-type": "error",
221
+ "jsdoc/require-returns-type": "error",
222
+ ...overrides,
223
+ }
224
+ }
225
+ },
226
+
227
+ /**
228
+ * Language options configuration
229
+ * @param {object} options - Configuration options
230
+ * @returns {object} Config object
231
+ */
232
+ "languageOptions": (options = {}) => {
233
+ const {
234
+ ecmaVersion = "latest",
235
+ sourceType = "module",
236
+ additionalGlobals = {},
237
+ } = options
238
+
239
+ return {
240
+ name: "gesslar/uglier/languageOptions",
241
+ languageOptions: {
242
+ ecmaVersion,
243
+ sourceType,
244
+ globals: additionalGlobals,
245
+ },
246
+ }
247
+ },
248
+
249
+ /**
250
+ * Browser/web globals configuration
251
+ * @param {object} options - Configuration options
252
+ * @returns {object} Config object
253
+ */
254
+ "web": (options = {}) => {
255
+ const {
256
+ files = ["src/**/*.{js,mjs,cjs}"],
257
+ additionalGlobals = {},
258
+ } = options
259
+
260
+ return {
261
+ name: "gesslar/uglier/web",
262
+ files: Array.isArray(files) ? files : [files],
263
+ languageOptions: {
264
+ globals: {
265
+ ...globals.browser,
266
+ ...additionalGlobals,
267
+ }
268
+ }
269
+ }
270
+ },
271
+
272
+ /**
273
+ * VSCode extension globals
274
+ * @param {object} options - Configuration options
275
+ * @returns {object} Config object
276
+ */
277
+ "vscode-extension": (options = {}) => {
278
+ const {
279
+ files = ["src/**/*.{js,mjs,cjs}"],
280
+ additionalGlobals = {},
281
+ } = options
282
+
283
+ return {
284
+ name: "gesslar/uglier/vscode-extension",
285
+ files: Array.isArray(files) ? files : [files],
286
+ languageOptions: {
287
+ globals: {
288
+ acquireVsCodeApi: "readonly",
289
+ ...additionalGlobals,
290
+ }
291
+ }
292
+ }
293
+ },
294
+
295
+ /**
296
+ * Node.js globals
297
+ * @param {object} options - Configuration options
298
+ * @returns {object} Config object
299
+ */
300
+ "node": (options = {}) => {
301
+ const {
302
+ files = ["**/*.{js,mjs,cjs}"],
303
+ additionalGlobals = {},
304
+ } = options
305
+
306
+ return {
307
+ name: "gesslar/uglier/node",
308
+ files: Array.isArray(files) ? files : [files],
309
+ languageOptions: {
310
+ globals: {
311
+ ...globals.node,
312
+ fetch: "readonly",
313
+ Headers: "readonly",
314
+ ...additionalGlobals,
315
+ }
316
+ }
317
+ }
318
+ },
319
+
320
+ /**
321
+ * CommonJS file override
322
+ * @param {object} options - Configuration options
323
+ * @returns {object} Config object
324
+ */
325
+ "cjs-override": (options = {}) => {
326
+ const {
327
+ files = ["**/*.cjs"],
328
+ } = options
329
+
330
+ return {
331
+ name: "gesslar/uglier/cjs-override",
332
+ files: Array.isArray(files) ? files : [files],
333
+ languageOptions: {
334
+ sourceType: "script",
335
+ ecmaVersion: 2021
336
+ },
337
+ }
338
+ },
339
+
340
+ /**
341
+ * ES Module file override
342
+ * @param {object} options - Configuration options
343
+ * @returns {object} Config object
344
+ */
345
+ "mjs-override": (options = {}) => {
346
+ const {
347
+ files = ["**/*.mjs"],
348
+ } = options
349
+
350
+ return {
351
+ name: "gesslar/uglier/mjs-override",
352
+ files: Array.isArray(files) ? files : [files],
353
+ languageOptions: {
354
+ sourceType: "module",
355
+ ecmaVersion: 2021
356
+ }
357
+ }
358
+ },
359
+
360
+ /**
361
+ * Tauri application configuration (browser + Tauri APIs, no Node.js)
362
+ * @param {object} options - Configuration options
363
+ * @returns {object} Config object
364
+ */
365
+ "tauri": (options = {}) => {
366
+ const {
367
+ files = ["src/**/*.{js,mjs,cjs}"],
368
+ additionalGlobals = {},
369
+ } = options
370
+
371
+ return {
372
+ name: "gesslar/uglier/tauri",
373
+ files: Array.isArray(files) ? files : [files],
374
+ languageOptions: {
375
+ globals: {
376
+ ...globals.browser,
377
+ __TAURI__: "readonly",
378
+ __TAURI_METADATA__: "readonly",
379
+ ...additionalGlobals,
380
+ }
381
+ }
382
+ }
383
+ },
384
+ }
385
+
386
+ /**
387
+ * Compose ESLint configuration from named config blocks
388
+ * @param {object} options - Composition options
389
+ * @param {string[]} options.with - Config names to include
390
+ * @param {string[]} options.without - Config names to exclude (higher precedence)
391
+ * @param {object} options.overrides - Per-config options overrides
392
+ * @returns {Array} ESLint flat config array
393
+ */
394
+ export default function(options = {}) {
395
+ const {
396
+ with: includeConfigs = ["lints-js", "lints-jsdoc"],
397
+ without: excludeConfigs = [],
398
+ overrides = {},
399
+ } = options
400
+
401
+ const configs = []
402
+
403
+ for(const configName of includeConfigs) {
404
+ if(excludeConfigs.includes(configName)) {
405
+ continue
406
+ }
407
+
408
+ if(!CONFIGS[configName]) {
409
+ throw new Error(
410
+ `Unknown config: "${configName}". Available: ${Object.keys(CONFIGS).join(", ")}`
411
+ )
412
+ }
413
+
414
+ const configOptions = overrides[configName] || {}
415
+ const config = CONFIGS[configName](configOptions)
416
+
417
+ configs.push(config)
418
+ }
419
+
420
+ return configs
421
+ }
422
+
423
+ /**
424
+ * Export all available config names for reference
425
+ */
426
+ export const availableConfigs = Object.keys(CONFIGS)