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