@favorodera/eslint-config 0.0.1 → 0.0.3

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/dist/index.mjs CHANGED
@@ -1,61 +1,23 @@
1
- import { FlatConfigComposer } from "eslint-flat-config-utils";
1
+ import { FlatConfigComposer, renamePluginsInRules } from "eslint-flat-config-utils";
2
2
  import { defu } from "defu";
3
3
  import { mergeProcessors, processorPassThrough } from "eslint-merge-processors";
4
+ import vueBlocksProcessor from "eslint-processor-vue-blocks";
4
5
  import globals from "globals";
5
- //#region src/utils.ts
6
- /**
7
- * Return the default export from a module-like object.
8
- * If the module has no default export, return the original resolved value.
9
- *
10
- * @param module - module or promise resolving to a module
11
- * @returns resolved module default or original module
12
- */
13
- async function getModuleDefault(module) {
14
- const resolvedModule = await module;
15
- return resolvedModule.default || resolvedModule;
16
- }
17
- /**
18
- * Normalize boolean or options value into merged options or null.
19
- * Returns null when the input is false or undefined.
20
- *
21
- * @param value - boolean, options object, or undefined
22
- * @param defaults - defaults to merge with
23
- * @returns merged options or false
24
- */
25
- function resolveOptions(value, defaults) {
26
- if (!value) return false;
27
- return defu(value === true ? {} : value, defaults);
28
- }
29
- /**
30
- * Rename rules by replacing configured plugin prefixes.
31
- *
32
- * @param rules - rules object to rename
33
- * @param map - prefix map used for renaming
34
- * @returns renamed rules object
35
- */
36
- function renameRules(rules, map) {
37
- return Object.fromEntries(Object.entries(rules).map(([key, value]) => {
38
- for (const [from, to] of Object.entries(map)) if (key.startsWith(`${from}/`)) return [to + key.slice(from.length), value];
39
- return [key, value];
40
- }));
41
- }
42
- //#endregion
43
6
  //#region src/globs.ts
44
- /** Glob pattern for JavaScript linting */
45
- const jsGlob = "**/*.?([cm])js";
46
- /** Glob pattern for TypeScript linting */
47
- const tsGlob = "**/*.?([cm])ts";
48
- /** Glob pattern for Vue linting */
7
+ const jsGlob = "**/*.{js,cjs,mjs}";
8
+ const tsGlob = "**/*.{ts,cts,mts}";
49
9
  const vueGlob = "**/*.vue";
50
- /** Glob pattern for Markdown linting */
51
10
  const mdGlob = "**/*.md";
52
- /**
53
- * Default glob patterns for files and directories to ignore
54
- * Includes common build outputs, dependencies, caches, and temporary files
55
- */
11
+ const mdInMdGlob = "**/*.md/*.md";
12
+ const codeInMdGlob = "**/*.md/**/*.{js,cjs,mjs,ts,cts,mts,vue}";
13
+ const jsonGlob = "**/*.json";
14
+ const json5Glob = "**/*.json5";
15
+ const jsoncGlob = "**/*.jsonc";
16
+ const tsConfigGlob = ["**/tsconfig.json", "**/tsconfig.*.json"];
17
+ const packageJsonGlob = "**/package.json";
56
18
  const ignoresGlob = [
57
- "**/node_modules",
58
- "**/dist",
19
+ "**/node_modules/**",
20
+ "**/dist/**",
59
21
  "**/package-lock.json",
60
22
  "**/yarn.lock",
61
23
  "**/pnpm-lock.yaml",
@@ -91,6 +53,38 @@ const ignoresGlob = [
91
53
  "**/.*/skills"
92
54
  ];
93
55
  //#endregion
56
+ //#region src/utils.ts
57
+ /**
58
+ * Resolves a module (or a promise of one) and returns its default export
59
+ * if present, otherwise returns the module itself.
60
+ *
61
+ * Handles both ESM modules (which wrap exports under `.default`) and
62
+ * CJS / plain-object modules uniformly.
63
+ *
64
+ * @param module - A module or a promise that resolves to one
65
+ * @returns The `.default` export if it exists, otherwise the module itself
66
+ */
67
+ async function importModule(module) {
68
+ const resolved = await module;
69
+ if (resolved !== null && typeof resolved === "object" && "default" in resolved) return resolved.default;
70
+ return resolved;
71
+ }
72
+ /**
73
+ * Normalize boolean or options value into merged options or null.
74
+ * Returns null when the input is false or undefined.
75
+ *
76
+ * @param value - boolean, options object, or undefined
77
+ * @param defaults - defaults to merge with
78
+ * @returns merged options or false
79
+ */
80
+ function resolveOptions(value, defaults) {
81
+ if (!value) return false;
82
+ return defu(value === true ? {} : value, defaults);
83
+ }
84
+ function extractRules(...configArrays) {
85
+ return Object.assign({}, ...configArrays.flat().map((config) => config?.rules || {}));
86
+ }
87
+ //#endregion
94
88
  //#region src/configs/vue.ts
95
89
  const sfcBlocksDefaults = { blocks: {
96
90
  styles: true,
@@ -101,41 +95,39 @@ const vueDefaults = {
101
95
  files: [vueGlob],
102
96
  sfcBlocks: sfcBlocksDefaults
103
97
  };
104
- /**
105
- * Vue SFC linting via `vue-eslint-parser`, `eslint-plugin-vue`, `typescript-eslint(parser)`.
106
- * @param options - Vue configuration options
107
- * @returns Promise resolving to Vue ESLint config items
108
- */
109
98
  async function vue(options) {
110
99
  const resolved = defu(options, vueDefaults);
111
100
  const sfcBlocks = resolveOptions(resolved.sfcBlocks, sfcBlocksDefaults);
112
- const [vuePlugin, vueParser, tsEsLint, vueBlocksProcessor] = await Promise.all([
113
- getModuleDefault(import("eslint-plugin-vue")),
114
- getModuleDefault(import("vue-eslint-parser")),
115
- getModuleDefault(import("typescript-eslint")),
116
- getModuleDefault(import("eslint-processor-vue-blocks"))
101
+ const [vuePlugin, vueParser, tsEsLint] = await Promise.all([
102
+ importModule(import("eslint-plugin-vue")),
103
+ importModule(import("vue-eslint-parser")),
104
+ importModule(import("typescript-eslint"))
117
105
  ]);
106
+ const baseRules = extractRules(vuePlugin.configs["flat/essential"], vuePlugin.configs["flat/strongly-recommended"], vuePlugin.configs["flat/recommended"]);
107
+ const processor = sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]);
118
108
  return [{
119
- name: "favorodera/vue/rules",
109
+ name: "favorodera/vue/setup",
120
110
  plugins: { vue: vuePlugin },
111
+ languageOptions: { globals: {
112
+ computed: "readonly",
113
+ defineEmits: "readonly",
114
+ defineExpose: "readonly",
115
+ defineProps: "readonly",
116
+ onMounted: "readonly",
117
+ onUnmounted: "readonly",
118
+ reactive: "readonly",
119
+ ref: "readonly",
120
+ shallowReactive: "readonly",
121
+ shallowRef: "readonly",
122
+ toRef: "readonly",
123
+ toRefs: "readonly",
124
+ watch: "readonly",
125
+ watchEffect: "readonly"
126
+ } }
127
+ }, {
128
+ name: "favorodera/vue/rules",
121
129
  files: resolved.files,
122
130
  languageOptions: {
123
- globals: {
124
- computed: "readonly",
125
- defineEmits: "readonly",
126
- defineExpose: "readonly",
127
- defineProps: "readonly",
128
- onMounted: "readonly",
129
- onUnmounted: "readonly",
130
- reactive: "readonly",
131
- ref: "readonly",
132
- shallowReactive: "readonly",
133
- shallowRef: "readonly",
134
- toRef: "readonly",
135
- toRefs: "readonly",
136
- watch: "readonly",
137
- watchEffect: "readonly"
138
- },
139
131
  parser: vueParser,
140
132
  parserOptions: {
141
133
  parser: tsEsLint.parser,
@@ -143,21 +135,9 @@ async function vue(options) {
143
135
  sourceType: "module"
144
136
  }
145
137
  },
146
- processor: sfcBlocks === false ? vuePlugin.processors[".vue"] : mergeProcessors([vuePlugin.processors[".vue"], vueBlocksProcessor(sfcBlocks)]),
138
+ processor,
147
139
  rules: {
148
- ...vuePlugin.configs.base.rules,
149
- ...vuePlugin.configs["flat/essential"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
150
- ...accumulator,
151
- ...currentConfig
152
- }), {}),
153
- ...vuePlugin.configs["flat/strongly-recommended"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
154
- ...accumulator,
155
- ...currentConfig
156
- }), {}),
157
- ...vuePlugin.configs["flat/recommended"].map((config) => config.rules).reduce((accumulator, currentConfig) => ({
158
- ...accumulator,
159
- ...currentConfig
160
- }), {}),
140
+ ...baseRules,
161
141
  "vue/block-order": ["error", { order: [
162
142
  "script",
163
143
  "template",
@@ -179,82 +159,196 @@ async function vue(options) {
179
159
  }];
180
160
  }
181
161
  //#endregion
182
- //#region src/configs/typescript.ts
183
- const typescriptDefaults = { files: [tsGlob] };
162
+ //#region src/configs/ignores.ts
163
+ const defaultPatterns = ignoresGlob;
184
164
  /**
185
- * Extract and merge rules from a compatible config array.
186
- * @param configs - Array of compatible ESLint configs
187
- * @returns Merged rules object from all configs
165
+ * Globs for ignoring files and directories from ESLint scanning.
166
+ * @param patterns - Additional ignore patterns or a function to modify the defaults.
167
+ * @returns An array containing the ignore flat config item.
188
168
  */
189
- function extractRules(configs) {
190
- return Object.assign({}, ...configs.map((config) => config.rules || {}));
169
+ function ignores(patterns = []) {
170
+ return [{
171
+ name: "favorodera/ignores",
172
+ ignores: typeof patterns === "function" ? patterns(defaultPatterns) : [...defaultPatterns, ...patterns]
173
+ }];
191
174
  }
192
- /**
193
- * Typescript linting via `typescript-eslint`.
194
- * @param options - TypeScript configuration options
195
- * @returns Promise resolving to TypeScript ESLint config items
196
- */
197
- async function typescript(options) {
198
- const resolved = defu(options, typescriptDefaults);
199
- const tsEsLint = await getModuleDefault(import("typescript-eslint"));
175
+ //#endregion
176
+ //#region src/configs/imports.ts
177
+ const importsDefaults = { files: [
178
+ jsGlob,
179
+ tsGlob,
180
+ vueGlob
181
+ ] };
182
+ async function imports(options) {
183
+ const resolved = defu(options, importsDefaults);
184
+ const [importPlugin, unusedImportsPlugin] = await Promise.all([importModule(import("eslint-plugin-import-lite")), importModule(import("eslint-plugin-unused-imports"))]);
185
+ const baseRules = importPlugin.configs.recommended?.rules || {};
200
186
  return [{
201
- name: "favorodera/typescript/rules",
187
+ name: "favorodera/imports/setup",
188
+ plugins: {
189
+ "import": importPlugin,
190
+ "unused-imports": unusedImportsPlugin
191
+ }
192
+ }, {
193
+ name: "favorodera/imports/rules",
202
194
  files: resolved.files,
203
- plugins: { ts: tsEsLint.plugin },
204
- languageOptions: {
205
- parser: tsEsLint.parser,
206
- parserOptions: { sourceType: "module" }
207
- },
208
195
  rules: {
209
- ...renameRules(extractRules(tsEsLint.configs.strict), { "@typescript-eslint": "ts" }),
210
- ...renameRules(extractRules(tsEsLint.configs.stylistic), { "@typescript-eslint": "ts" }),
211
- "ts/consistent-type-imports": ["error", { prefer: "type-imports" }],
212
- "ts/no-empty-object-type": ["error", { allowInterfaces: "with-single-extends" }],
213
- "ts/array-type": ["error", { default: "array" }],
196
+ ...renamePluginsInRules(baseRules, { "import-lite": "import" }),
197
+ "import/consistent-type-specifier-style": ["error", "top-level"],
198
+ "import/first": "error",
199
+ "import/no-duplicates": "error",
200
+ "import/no-mutable-exports": "error",
201
+ "import/no-named-default": "error",
202
+ "import/newline-after-import": ["error", { count: 1 }],
203
+ "unused-imports/no-unused-imports": "error",
204
+ "unused-imports/no-unused-vars": ["error", {
205
+ args: "after-used",
206
+ argsIgnorePattern: "^_",
207
+ ignoreRestSiblings: true,
208
+ vars: "all",
209
+ varsIgnorePattern: "^_"
210
+ }],
214
211
  ...resolved.overrides
215
212
  }
216
213
  }];
217
214
  }
218
215
  //#endregion
219
- //#region src/configs/ignores.ts
220
- const defaultPatterns = ignoresGlob;
216
+ //#region src/configs/javascript.ts
217
+ const javascriptDefaults = { files: [
218
+ jsGlob,
219
+ tsGlob,
220
+ vueGlob
221
+ ] };
221
222
  /**
222
- * Globs for ignoring files and directories from ESLint scanning.
223
- * @param patterns - Additional ignore patterns or a function to modify the defaults.
224
- * @returns An array containing the ignore flat config item.
223
+ * Javascript linting via `eslint`
224
+ * @param options - Javascript configuration options
225
+ * @returns Promise resolving to javascript ESLint config items
225
226
  */
226
- function ignores(patterns = []) {
227
+ async function javascript(options) {
228
+ const resolved = defu(options, javascriptDefaults);
229
+ const baseRules = (await importModule(import("@eslint/js"))).configs.recommended.rules;
227
230
  return [{
228
- name: "favorodera/ignores",
229
- ignores: typeof patterns === "function" ? patterns(defaultPatterns) : [...defaultPatterns, ...patterns]
231
+ name: "favorodera/javascript/setup",
232
+ languageOptions: {
233
+ ecmaVersion: "latest",
234
+ sourceType: "module",
235
+ globals: {
236
+ ...globals.browser,
237
+ ...globals.es2021,
238
+ ...globals.node,
239
+ document: "readonly",
240
+ navigator: "readonly",
241
+ window: "readonly"
242
+ }
243
+ },
244
+ linterOptions: { reportUnusedDisableDirectives: true }
245
+ }, {
246
+ name: "favorodera/javascript/rules",
247
+ files: resolved.files,
248
+ rules: {
249
+ ...baseRules,
250
+ "accessor-pairs": ["error", {
251
+ enforceForClassMembers: true,
252
+ setWithoutGet: true
253
+ }],
254
+ "array-callback-return": "error",
255
+ "block-scoped-var": "error",
256
+ ...resolved.overrides
257
+ }
230
258
  }];
231
259
  }
232
260
  //#endregion
261
+ //#region src/configs/markdown.ts
262
+ const markdownDefaults = {
263
+ files: [mdGlob],
264
+ gfm: true
265
+ };
266
+ async function markdown(options) {
267
+ const resolved = defu(options, markdownDefaults);
268
+ const markdownPlugin = await importModule(import("@eslint/markdown"));
269
+ const baseRules = extractRules(markdownPlugin.configs.recommended);
270
+ return [
271
+ {
272
+ name: "favorodera/markdown/setup",
273
+ plugins: { md: markdownPlugin }
274
+ },
275
+ {
276
+ name: "favorodera/markdown/rules",
277
+ files: resolved.files,
278
+ language: resolved.gfm ? "md/gfm" : "md/commonmark",
279
+ ignores: [mdInMdGlob],
280
+ processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
281
+ rules: {
282
+ ...renamePluginsInRules(baseRules, { markdown: "md" }),
283
+ "md/fenced-code-language": "off",
284
+ "md/no-missing-label-refs": "off",
285
+ ...resolved.overrides
286
+ }
287
+ },
288
+ {
289
+ name: "favorodera/markdown/disables/code",
290
+ files: [codeInMdGlob],
291
+ languageOptions: { parserOptions: { ecmaFeatures: { impliedStrict: true } } },
292
+ rules: {
293
+ "no-alert": "off",
294
+ "no-console": "off",
295
+ "no-labels": "off",
296
+ "no-lone-blocks": "off",
297
+ "no-restricted-syntax": "off",
298
+ "no-undef": "off",
299
+ "no-unused-expressions": "off",
300
+ "no-unused-labels": "off",
301
+ "no-unused-vars": "off",
302
+ "node/prefer-global/process": "off",
303
+ "style/comma-dangle": "off",
304
+ "style/eol-last": "off",
305
+ "style/padding-line-between-statements": "off",
306
+ "ts/consistent-type-imports": "off",
307
+ "ts/explicit-function-return-type": "off",
308
+ "ts/no-namespace": "off",
309
+ "ts/no-redeclare": "off",
310
+ "ts/no-require-imports": "off",
311
+ "ts/no-unused-expressions": "off",
312
+ "ts/no-unused-vars": "off",
313
+ "ts/no-use-before-define": "off",
314
+ "unicode-bom": "off",
315
+ "unused-imports/no-unused-imports": "off",
316
+ "unused-imports/no-unused-vars": "off"
317
+ }
318
+ }
319
+ ];
320
+ }
321
+ //#endregion
233
322
  //#region src/configs/stylistic.ts
234
323
  const stylisticDefaults = {
235
- indent: 2,
236
- experimental: false,
237
- quotes: "single",
238
- semi: false,
239
- jsx: false
324
+ settings: {
325
+ indent: 2,
326
+ experimental: false,
327
+ quotes: "single",
328
+ semi: false,
329
+ jsx: false
330
+ },
331
+ files: [
332
+ jsGlob,
333
+ tsGlob,
334
+ vueGlob
335
+ ]
240
336
  };
241
- /**
242
- * Code style via `@stylistic/eslint-plugin`.
243
- * @param options - Stylistic configuration options
244
- * @returns Promise resolving to stylistic ESLint config items
245
- */
246
337
  async function stylistic(options) {
247
338
  const resolved = defu(options, stylisticDefaults);
248
- const stylePlugin = await getModuleDefault(import("@stylistic/eslint-plugin"));
249
- const customizedStyleConfig = stylePlugin.configs.customize({
339
+ const stylePlugin = await importModule(import("@stylistic/eslint-plugin"));
340
+ const baseRules = stylePlugin.configs.customize({
250
341
  pluginName: "style",
251
- ...resolved
252
- });
342
+ ...resolved.settings
343
+ })?.rules || {};
253
344
  return [{
345
+ name: "favorodera/stylistic/setup",
346
+ plugins: { style: stylePlugin }
347
+ }, {
254
348
  name: "favorodera/stylistic/rules",
255
- plugins: { style: stylePlugin },
349
+ files: resolved.files,
256
350
  rules: {
257
- ...customizedStyleConfig.rules,
351
+ ...baseRules,
258
352
  "style/quotes": [
259
353
  "error",
260
354
  "single",
@@ -297,15 +391,20 @@ const tailwindDefaults = {
297
391
  */
298
392
  async function tailwind(options) {
299
393
  const resolved = defu(options, tailwindDefaults);
300
- const tailwindPlugin = await getModuleDefault(import("eslint-plugin-better-tailwindcss"));
394
+ const tailwindPlugin = await importModule(import("eslint-plugin-better-tailwindcss"));
395
+ const baseRules = {
396
+ ...tailwindPlugin.configs["recommended-error"].rules,
397
+ ...tailwindPlugin.configs["stylistic-error"].rules
398
+ };
301
399
  return [{
302
- name: "favorodera/tailwind/rules",
400
+ name: "favorodera/tailwind/setup",
303
401
  plugins: { tailwind: tailwindPlugin },
402
+ settings: { tailwindcss: resolved.settings }
403
+ }, {
404
+ name: "favorodera/tailwind/rules",
304
405
  files: resolved.files,
305
- settings: { tailwindcss: resolved.settings },
306
406
  rules: {
307
- ...renameRules(tailwindPlugin.configs["recommended-error"].rules, { "better-tailwindcss": "tailwind" }),
308
- ...renameRules(tailwindPlugin.configs["stylistic-error"].rules, { "better-tailwindcss": "tailwind" }),
407
+ ...renamePluginsInRules(baseRules, { "better-tailwindcss": "tailwind" }),
309
408
  "tailwind/no-unregistered-classes": "off",
310
409
  "tailwind/enforce-consistent-line-wrapping": ["error", { group: "emptyLine" }],
311
410
  ...resolved.overrides
@@ -313,321 +412,321 @@ async function tailwind(options) {
313
412
  }];
314
413
  }
315
414
  //#endregion
316
- //#region src/configs/comments.ts
317
- const commentsDefaults = { files: [
318
- jsGlob,
319
- tsGlob,
320
- vueGlob
321
- ] };
322
- /**
323
- * Comments linting via `@eslint-community/eslint-plugin-eslint-comments`
324
- * @param options - Comments configuration options
325
- * @returns Promise resolving to comments ESLint config items
326
- */
327
- async function comments(options) {
328
- const resolved = defu(options, commentsDefaults);
329
- const commentsPlugin = await getModuleDefault(import("@eslint-community/eslint-plugin-eslint-comments"));
330
- return [{
331
- name: "favorodera/comments/rules",
332
- files: resolved.files,
333
- plugins: { comments: commentsPlugin },
334
- rules: {
335
- ...renameRules(commentsPlugin.configs.recommended?.rules || {}, { "@eslint-community/eslint-comments": "comments" }),
336
- "comments/no-aggregating-enable": "error",
337
- "comments/no-duplicate-disable": "error",
338
- "comments/no-unlimited-disable": "error",
339
- "comments/no-unused-enable": "error",
340
- ...resolved.overrides
341
- }
342
- }];
343
- }
344
- //#endregion
345
- //#region src/configs/imports.ts
346
- const importsDefaults = { files: [
347
- jsGlob,
348
- tsGlob,
349
- vueGlob
350
- ] };
415
+ //#region src/configs/typescript.ts
416
+ const typescriptDefaults = { files: [tsGlob] };
351
417
  /**
352
- * Imports & unused imports linting via `eslint-plugin-import-lite` and `eslint-plugin-unused-imports`.
353
- * @param options - Imports & unused imports configuration options
354
- * @returns Promise resolving to imports & unused imports ESLint config items
418
+ * Typescript linting via `typescript-eslint`.
419
+ * @param options - TypeScript configuration options
420
+ * @returns Array of TypeScript ESLint config items
355
421
  */
356
- async function imports(options) {
357
- const resolved = defu(options, importsDefaults);
358
- const [importPlugin, unusedImportsPlugin] = await Promise.all([getModuleDefault(import("eslint-plugin-import-lite")), getModuleDefault(import("eslint-plugin-unused-imports"))]);
422
+ async function typescript(options) {
423
+ const resolved = defu(options, typescriptDefaults);
424
+ const tsEsLint = await importModule(import("typescript-eslint"));
425
+ const baseRules = extractRules(tsEsLint.configs.recommended, tsEsLint.configs.strict);
359
426
  return [{
360
- name: "favorodera/imports/rules",
427
+ name: "favorodera/typescript/setup",
428
+ plugins: { ts: tsEsLint.plugin }
429
+ }, {
430
+ name: "favorodera/typescript/rules",
361
431
  files: resolved.files,
362
- plugins: {
363
- "import": importPlugin,
364
- "unused-imports": unusedImportsPlugin
432
+ languageOptions: {
433
+ parser: tsEsLint.parser,
434
+ parserOptions: { sourceType: "module" }
365
435
  },
366
436
  rules: {
367
- ...renameRules(importPlugin.configs.recommended?.rules || {}, { "import-lite": "import" }),
368
- "import/consistent-type-specifier-style": ["error", "top-level"],
369
- "import/first": "error",
370
- "import/no-duplicates": "error",
371
- "import/no-mutable-exports": "error",
372
- "import/no-named-default": "error",
373
- "import/newline-after-import": ["error", { count: 1 }],
374
- "unused-imports/no-unused-imports": "error",
375
- "unused-imports/no-unused-vars": ["error", {
376
- args: "after-used",
377
- argsIgnorePattern: "^_",
378
- ignoreRestSiblings: true,
379
- vars: "all",
380
- varsIgnorePattern: "^_"
437
+ ...renamePluginsInRules(baseRules, { "@typescript-eslint": "ts" }),
438
+ "ts/consistent-type-imports": ["error", { prefer: "type-imports" }],
439
+ "ts/no-empty-object-type": ["error", { allowInterfaces: "with-single-extends" }],
440
+ "ts/array-type": ["error", {
441
+ default: "generic",
442
+ readonly: "generic"
381
443
  }],
382
444
  ...resolved.overrides
383
445
  }
384
446
  }];
385
447
  }
386
448
  //#endregion
387
- //#region src/configs/markdown.ts
388
- const markdownDefaults = {
389
- files: [mdGlob],
390
- gfm: true
391
- };
392
- /**
393
- * Markdown linting via `@eslint/markdown`.
394
- * @param options - Markdown configuration options
395
- * @returns Promise resolving to Markdown ESLint config items
396
- */
397
- async function markdown(options) {
398
- const resolved = defu(options, markdownDefaults);
399
- const markdownPlugin = await getModuleDefault(import("@eslint/markdown"));
400
- return [{
401
- name: "favorodera/markdown/rules",
402
- files: resolved.files,
403
- plugins: { md: markdownPlugin },
404
- processor: mergeProcessors([markdownPlugin.processors?.markdown, processorPassThrough]),
405
- language: resolved.gfm ? "md/gfm" : "md/commonmark",
406
- rules: {
407
- ...renameRules(markdownPlugin.configs.recommended[0]?.rules || {}, { markdown: "md" }),
408
- "md/fenced-code-language": "off",
409
- "md/no-missing-label-refs": "off",
410
- ...resolved.overrides
411
- }
412
- }];
413
- }
414
- //#endregion
415
- //#region src/configs/javascript.ts
416
- const javascriptDefaults = { files: [
417
- jsGlob,
418
- tsGlob,
419
- vueGlob
449
+ //#region src/configs/jsonc.ts
450
+ const jsoncDefaults = { files: [
451
+ json5Glob,
452
+ jsoncGlob,
453
+ jsonGlob
420
454
  ] };
421
- /**
422
- * Javascript linting via `eslint`
423
- * @param options - Javascript configuration options
424
- * @returns Promise resolving to javascript ESLint config items
425
- */
426
- async function javascript(options) {
427
- const resolved = defu(options, javascriptDefaults);
428
- const jsPlugin = await getModuleDefault(import("@eslint/js"));
429
- return [{
430
- name: "favorodera/javascript/rules",
431
- files: resolved.files,
432
- languageOptions: {
433
- ecmaVersion: "latest",
434
- sourceType: "module",
435
- globals: {
436
- ...globals.browser,
437
- ...globals.es2021,
438
- ...globals.node,
439
- document: "readonly",
440
- navigator: "readonly",
441
- window: "readonly"
455
+ async function jsonc(options) {
456
+ const resolved = defu(options, jsoncDefaults);
457
+ return [
458
+ {
459
+ name: "favorodera/jsonc/setup",
460
+ plugins: { jsonc: await importModule(import("eslint-plugin-jsonc")) }
461
+ },
462
+ {
463
+ name: "favorodera/jsonc/rules",
464
+ files: resolved.files,
465
+ language: "jsonc/x",
466
+ rules: {
467
+ "jsonc/no-bigint-literals": "error",
468
+ "jsonc/no-binary-expression": "error",
469
+ "jsonc/no-binary-numeric-literals": "error",
470
+ "jsonc/no-dupe-keys": "error",
471
+ "jsonc/no-escape-sequence-in-identifier": "error",
472
+ "jsonc/no-floating-decimal": "error",
473
+ "jsonc/no-hexadecimal-numeric-literals": "error",
474
+ "jsonc/no-infinity": "error",
475
+ "jsonc/no-multi-str": "error",
476
+ "jsonc/no-nan": "error",
477
+ "jsonc/no-number-props": "error",
478
+ "jsonc/no-numeric-separators": "error",
479
+ "jsonc/no-octal": "error",
480
+ "jsonc/no-octal-escape": "error",
481
+ "jsonc/no-octal-numeric-literals": "error",
482
+ "jsonc/no-parenthesized": "error",
483
+ "jsonc/no-plus-sign": "error",
484
+ "jsonc/no-regexp-literals": "error",
485
+ "jsonc/no-sparse-arrays": "error",
486
+ "jsonc/no-template-literals": "error",
487
+ "jsonc/no-undefined-value": "error",
488
+ "jsonc/no-unicode-codepoint-escapes": "error",
489
+ "jsonc/no-useless-escape": "error",
490
+ "jsonc/space-unary-ops": "error",
491
+ "jsonc/valid-json-number": "error",
492
+ "jsonc/vue-custom-block/no-parsing-error": "error",
493
+ "jsonc/array-bracket-spacing": ["error", "never"],
494
+ "jsonc/comma-dangle": ["error", "never"],
495
+ "jsonc/comma-style": ["error", "last"],
496
+ "jsonc/indent": ["error", 2],
497
+ "jsonc/key-spacing": ["error", {
498
+ afterColon: true,
499
+ beforeColon: false
500
+ }],
501
+ "jsonc/object-curly-newline": ["error", {
502
+ consistent: true,
503
+ multiline: true
504
+ }],
505
+ "jsonc/object-curly-spacing": ["error", "always"],
506
+ "jsonc/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
507
+ "jsonc/quote-props": "error",
508
+ "jsonc/quotes": "error",
509
+ ...resolved.overrides
442
510
  }
443
511
  },
444
- linterOptions: { reportUnusedDisableDirectives: true },
445
- plugins: { js: jsPlugin },
446
- rules: {
447
- ...jsPlugin.configs.recommended.rules,
448
- "accessor-pairs": ["error", {
449
- enforceForClassMembers: true,
450
- setWithoutGet: true
451
- }],
452
- "array-callback-return": "error",
453
- "block-scoped-var": "error",
454
- "constructor-super": "error",
455
- "default-case-last": "error",
456
- "dot-notation": ["error", { allowKeywords: true }],
457
- "eqeqeq": ["error", "smart"],
458
- "new-cap": ["error", {
459
- capIsNew: false,
460
- newIsCap: true,
461
- properties: true
462
- }],
463
- "no-alert": "error",
464
- "no-array-constructor": "error",
465
- "no-async-promise-executor": "error",
466
- "no-caller": "error",
467
- "no-case-declarations": "error",
468
- "no-class-assign": "error",
469
- "no-compare-neg-zero": "error",
470
- "no-cond-assign": ["error", "always"],
471
- "no-console": ["error", { allow: ["warn", "error"] }],
472
- "no-const-assign": "error",
473
- "no-control-regex": "error",
474
- "no-debugger": "error",
475
- "no-delete-var": "error",
476
- "no-dupe-args": "error",
477
- "no-dupe-class-members": "error",
478
- "no-dupe-keys": "error",
479
- "no-duplicate-case": "error",
480
- "no-empty": ["error", { allowEmptyCatch: true }],
481
- "no-empty-character-class": "error",
482
- "no-empty-pattern": "error",
483
- "no-eval": "error",
484
- "no-ex-assign": "error",
485
- "no-extend-native": "error",
486
- "no-extra-bind": "error",
487
- "no-extra-boolean-cast": "error",
488
- "no-fallthrough": "error",
489
- "no-func-assign": "error",
490
- "no-global-assign": "error",
491
- "no-implied-eval": "error",
492
- "no-import-assign": "error",
493
- "no-invalid-regexp": "error",
494
- "no-irregular-whitespace": "error",
495
- "no-iterator": "error",
496
- "no-labels": ["error", {
497
- allowLoop: false,
498
- allowSwitch: false
499
- }],
500
- "no-lone-blocks": "error",
501
- "no-loss-of-precision": "error",
502
- "no-misleading-character-class": "error",
503
- "no-multi-str": "error",
504
- "no-new": "error",
505
- "no-new-func": "error",
506
- "no-new-native-nonconstructor": "error",
507
- "no-new-wrappers": "error",
508
- "no-obj-calls": "error",
509
- "no-octal": "error",
510
- "no-octal-escape": "error",
511
- "no-proto": "error",
512
- "no-prototype-builtins": "error",
513
- "no-redeclare": ["error", { builtinGlobals: false }],
514
- "no-regex-spaces": "error",
515
- "no-restricted-globals": [
512
+ {
513
+ name: "favorodera/jsonc/sort/package-json",
514
+ files: [packageJsonGlob],
515
+ rules: {
516
+ "jsonc/sort-array-values": ["error", {
517
+ order: { type: "asc" },
518
+ pathPattern: "^files$"
519
+ }],
520
+ "jsonc/sort-keys": [
521
+ "error",
522
+ {
523
+ order: [
524
+ "publisher",
525
+ "name",
526
+ "displayName",
527
+ "type",
528
+ "version",
529
+ "private",
530
+ "packageManager",
531
+ "description",
532
+ "author",
533
+ "contributors",
534
+ "license",
535
+ "funding",
536
+ "homepage",
537
+ "repository",
538
+ "bugs",
539
+ "keywords",
540
+ "categories",
541
+ "sideEffects",
542
+ "imports",
543
+ "exports",
544
+ "main",
545
+ "module",
546
+ "unpkg",
547
+ "jsdelivr",
548
+ "types",
549
+ "typesVersions",
550
+ "bin",
551
+ "icon",
552
+ "files",
553
+ "engines",
554
+ "activationEvents",
555
+ "contributes",
556
+ "scripts",
557
+ "peerDependencies",
558
+ "peerDependenciesMeta",
559
+ "dependencies",
560
+ "optionalDependencies",
561
+ "devDependencies",
562
+ "pnpm",
563
+ "overrides",
564
+ "resolutions",
565
+ "husky",
566
+ "simple-git-hooks",
567
+ "lint-staged",
568
+ "eslintConfig"
569
+ ],
570
+ pathPattern: "^$"
571
+ },
572
+ {
573
+ order: { type: "asc" },
574
+ pathPattern: "^(?:dev|peer|optional|bundled)?[Dd]ependencies(Meta)?$"
575
+ },
576
+ {
577
+ order: { type: "asc" },
578
+ pathPattern: "^(?:resolutions|overrides|pnpm.overrides)$"
579
+ },
580
+ {
581
+ order: { type: "asc" },
582
+ pathPattern: "^workspaces\\.catalog$"
583
+ },
584
+ {
585
+ order: { type: "asc" },
586
+ pathPattern: "^workspaces\\.catalogs\\.[^.]+$"
587
+ },
588
+ {
589
+ order: [
590
+ "types",
591
+ "import",
592
+ "require",
593
+ "default"
594
+ ],
595
+ pathPattern: "^exports.*$"
596
+ },
597
+ {
598
+ order: [
599
+ "pre-commit",
600
+ "prepare-commit-msg",
601
+ "commit-msg",
602
+ "post-commit",
603
+ "pre-rebase",
604
+ "post-rewrite",
605
+ "post-checkout",
606
+ "post-merge",
607
+ "pre-push",
608
+ "pre-auto-gc"
609
+ ],
610
+ pathPattern: "^(?:gitHooks|husky|simple-git-hooks)$"
611
+ }
612
+ ]
613
+ }
614
+ },
615
+ {
616
+ name: "favorodera/jsonc/sort/tsconfig-json",
617
+ files: tsConfigGlob,
618
+ rules: { "jsonc/sort-keys": [
516
619
  "error",
517
620
  {
518
- message: "Use `globalThis` instead.",
519
- name: "global"
621
+ order: [
622
+ "extends",
623
+ "compilerOptions",
624
+ "references",
625
+ "files",
626
+ "include",
627
+ "exclude"
628
+ ],
629
+ pathPattern: "^$"
520
630
  },
521
631
  {
522
- message: "Use `globalThis` instead.",
523
- name: "self"
632
+ order: [
633
+ "incremental",
634
+ "composite",
635
+ "tsBuildInfoFile",
636
+ "disableSourceOfProjectReferenceRedirect",
637
+ "disableSolutionSearching",
638
+ "disableReferencedProjectLoad",
639
+ "target",
640
+ "jsx",
641
+ "jsxFactory",
642
+ "jsxFragmentFactory",
643
+ "jsxImportSource",
644
+ "lib",
645
+ "moduleDetection",
646
+ "noLib",
647
+ "reactNamespace",
648
+ "useDefineForClassFields",
649
+ "emitDecoratorMetadata",
650
+ "experimentalDecorators",
651
+ "libReplacement",
652
+ "baseUrl",
653
+ "rootDir",
654
+ "rootDirs",
655
+ "customConditions",
656
+ "module",
657
+ "moduleResolution",
658
+ "moduleSuffixes",
659
+ "noResolve",
660
+ "paths",
661
+ "resolveJsonModule",
662
+ "resolvePackageJsonExports",
663
+ "resolvePackageJsonImports",
664
+ "typeRoots",
665
+ "types",
666
+ "allowArbitraryExtensions",
667
+ "allowImportingTsExtensions",
668
+ "allowUmdGlobalAccess",
669
+ "allowJs",
670
+ "checkJs",
671
+ "maxNodeModuleJsDepth",
672
+ "strict",
673
+ "strictBindCallApply",
674
+ "strictFunctionTypes",
675
+ "strictNullChecks",
676
+ "strictPropertyInitialization",
677
+ "allowUnreachableCode",
678
+ "allowUnusedLabels",
679
+ "alwaysStrict",
680
+ "exactOptionalPropertyTypes",
681
+ "noFallthroughCasesInSwitch",
682
+ "noImplicitAny",
683
+ "noImplicitOverride",
684
+ "noImplicitReturns",
685
+ "noImplicitThis",
686
+ "noPropertyAccessFromIndexSignature",
687
+ "noUncheckedIndexedAccess",
688
+ "noUnusedLocals",
689
+ "noUnusedParameters",
690
+ "useUnknownInCatchVariables",
691
+ "declaration",
692
+ "declarationDir",
693
+ "declarationMap",
694
+ "downlevelIteration",
695
+ "emitBOM",
696
+ "emitDeclarationOnly",
697
+ "importHelpers",
698
+ "importsNotUsedAsValues",
699
+ "inlineSourceMap",
700
+ "inlineSources",
701
+ "mapRoot",
702
+ "newLine",
703
+ "noEmit",
704
+ "noEmitHelpers",
705
+ "noEmitOnError",
706
+ "outDir",
707
+ "outFile",
708
+ "preserveConstEnums",
709
+ "preserveValueImports",
710
+ "removeComments",
711
+ "sourceMap",
712
+ "sourceRoot",
713
+ "stripInternal",
714
+ "allowSyntheticDefaultImports",
715
+ "esModuleInterop",
716
+ "forceConsistentCasingInFileNames",
717
+ "isolatedDeclarations",
718
+ "isolatedModules",
719
+ "preserveSymlinks",
720
+ "verbatimModuleSyntax",
721
+ "erasableSyntaxOnly",
722
+ "skipDefaultLibCheck",
723
+ "skipLibCheck"
724
+ ],
725
+ pathPattern: "^compilerOptions$"
524
726
  }
525
- ],
526
- "no-restricted-properties": [
527
- "error",
528
- {
529
- message: "Use `Object.getPrototypeOf` or `Object.setPrototypeOf` instead.",
530
- property: "__proto__"
531
- },
532
- {
533
- message: "Use `Object.defineProperty` instead.",
534
- property: "__defineGetter__"
535
- },
536
- {
537
- message: "Use `Object.defineProperty` instead.",
538
- property: "__defineSetter__"
539
- },
540
- {
541
- message: "Use `Object.getOwnPropertyDescriptor` instead.",
542
- property: "__lookupGetter__"
543
- },
544
- {
545
- message: "Use `Object.getOwnPropertyDescriptor` instead.",
546
- property: "__lookupSetter__"
547
- }
548
- ],
549
- "no-restricted-syntax": [
550
- "error",
551
- "TSEnumDeclaration[const=true]",
552
- "TSExportAssignment"
553
- ],
554
- "no-self-assign": ["error", { props: true }],
555
- "no-self-compare": "error",
556
- "no-sequences": "error",
557
- "no-shadow-restricted-names": "error",
558
- "no-sparse-arrays": "error",
559
- "no-template-curly-in-string": "error",
560
- "no-this-before-super": "error",
561
- "no-throw-literal": "error",
562
- "no-undef": "error",
563
- "no-undef-init": "error",
564
- "no-unexpected-multiline": "error",
565
- "no-unmodified-loop-condition": "error",
566
- "no-unneeded-ternary": ["error", { defaultAssignment: false }],
567
- "no-unreachable": "error",
568
- "no-unreachable-loop": "error",
569
- "no-unsafe-finally": "error",
570
- "no-unsafe-negation": "error",
571
- "no-unused-expressions": ["error", {
572
- allowShortCircuit: true,
573
- allowTaggedTemplates: true,
574
- allowTernary: true
575
- }],
576
- "no-unused-vars": ["error", {
577
- args: "none",
578
- caughtErrors: "none",
579
- ignoreRestSiblings: true,
580
- vars: "all"
581
- }],
582
- "no-use-before-define": ["error", {
583
- classes: false,
584
- functions: false,
585
- variables: true
586
- }],
587
- "no-useless-backreference": "error",
588
- "no-useless-call": "error",
589
- "no-useless-catch": "error",
590
- "no-useless-computed-key": "error",
591
- "no-useless-constructor": "error",
592
- "no-useless-rename": "error",
593
- "no-useless-return": "error",
594
- "no-var": "error",
595
- "no-with": "error",
596
- "object-shorthand": [
597
- "error",
598
- "always",
599
- {
600
- avoidQuotes: true,
601
- ignoreConstructors: false
602
- }
603
- ],
604
- "one-var": ["error", { initialized: "never" }],
605
- "prefer-arrow-callback": ["error", {
606
- allowNamedFunctions: false,
607
- allowUnboundThis: true
608
- }],
609
- "prefer-const": ["error", {
610
- destructuring: "all",
611
- ignoreReadBeforeAssign: true
612
- }],
613
- "prefer-exponentiation-operator": "error",
614
- "prefer-promise-reject-errors": "error",
615
- "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
616
- "prefer-rest-params": "error",
617
- "prefer-spread": "error",
618
- "prefer-template": "error",
619
- "symbol-description": "error",
620
- "unicode-bom": ["error", "never"],
621
- "use-isnan": ["error", {
622
- enforceForIndexOf: true,
623
- enforceForSwitchCase: true
624
- }],
625
- "valid-typeof": ["error", { requireStringLiterals: true }],
626
- "vars-on-top": "error",
627
- "yoda": ["error", "never"],
628
- ...resolved.overrides
727
+ ] }
629
728
  }
630
- }];
729
+ ];
631
730
  }
632
731
  //#endregion
633
732
  //#region src/factory.ts
@@ -639,25 +738,29 @@ async function javascript(options) {
639
738
  function factory(options = {}) {
640
739
  const configs = [];
641
740
  configs.push(ignores(options.ignores));
642
- const vueOptions = resolveOptions(options.vue, {});
643
- const typescriptOptions = resolveOptions(options.typescript, {});
644
- const stylisticOptions = resolveOptions(options.stylistic, {});
645
- const tailwindOptions = resolveOptions(options.tailwind, {});
646
- const commentsOptions = resolveOptions(options.comments, {});
647
- const importsOptions = resolveOptions(options.imports, {});
648
- const markdownOptions = resolveOptions(options.markdown, {});
649
- const javascriptOptions = resolveOptions(options.javascript, {});
650
- if (vueOptions) configs.push(vue(vueOptions));
651
- if (typescriptOptions) configs.push(typescript(typescriptOptions));
652
- if (stylisticOptions) configs.push(stylistic(stylisticOptions));
653
- if (tailwindOptions) configs.push(tailwind(tailwindOptions));
654
- if (commentsOptions) configs.push(comments(commentsOptions));
655
- if (importsOptions) configs.push(imports(importsOptions));
656
- if (markdownOptions) configs.push(markdown(markdownOptions));
657
- if (javascriptOptions) configs.push(javascript(javascriptOptions));
741
+ const configFunctions = {
742
+ vue,
743
+ typescript,
744
+ stylistic,
745
+ tailwind,
746
+ imports,
747
+ markdown,
748
+ javascript,
749
+ jsonc
750
+ };
751
+ for (const [key, configFunction] of Object.entries(configFunctions)) {
752
+ const configOption = options[key];
753
+ const resolved = resolveOptions(configOption ?? true, {});
754
+ if (resolved) configs.push(configFunction(resolved));
755
+ }
658
756
  let composer = new FlatConfigComposer();
659
- composer = composer.append(...configs);
757
+ composer = composer.append(...configs).renamePlugins({
758
+ "better-tailwindcss": "tailwind",
759
+ "@typescript-eslint": "ts",
760
+ "markdown": "md",
761
+ "import-lite": "import"
762
+ });
660
763
  return composer;
661
764
  }
662
765
  //#endregion
663
- export { factory, getModuleDefault, renameRules };
766
+ export { factory, importModule };