@vijayhardaha/dev-config 2.1.0 → 2.1.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.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Reusable development configuration package for Next.js + TypeScript projects.
8
8
 
9
- > **v2.0.4** — Requires ESLint >=10. Native flat config only. No FlatCompat.
9
+ > **v2.1.0** — Requires ESLint >=10. Native flat config only. No FlatCompat.
10
10
 
11
11
  ## Features
12
12
 
@@ -27,7 +27,7 @@ bun add --dev @vijayhardaha/dev-config
27
27
  ### Install Required Packages
28
28
 
29
29
  ```bash
30
- bun add --dev eslint @eslint/compat eslint-config-prettier prettier @prettier/plugin-xml eslint-plugin-prettier globals eslint-plugin-jsdoc eslint-plugin-import-x @typescript-eslint/eslint-plugin @typescript-eslint/parser typescript typescript-eslint husky
30
+ bun add --dev eslint @eslint/compat @eslint/js eslint-config-prettier prettier @prettier/plugin-xml eslint-plugin-prettier globals eslint-plugin-jsdoc eslint-plugin-import-x @typescript-eslint/eslint-plugin @typescript-eslint/parser typescript typescript-eslint husky
31
31
  ```
32
32
 
33
33
  ### Install Optional Packages
@@ -56,6 +56,7 @@ bun add --dev eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-jsx-a1
56
56
 
57
57
  ```bash
58
58
  bun add --dev @next/eslint-plugin-next eslint-config-next
59
+ bun add --dev next
59
60
  ```
60
61
 
61
62
  #### Commitlint
@@ -79,15 +80,15 @@ v2 drops FlatCompat and uses native ESLint 10 flat configs throughout.
79
80
  - **ESLint 10 required** — no longer compatible with ESLint 8/9
80
81
  - **No FlatCompat** — all configs import flat config arrays/objects directly
81
82
  - **`eslint-plugin-import` → `eslint-plugin-import-x`** — the ESLint 10-compatible fork
82
- - **`eslint-config-prettier` removed** — bundled in `eslint-plugin-prettier/recommended`
83
- - **`@eslint/js` removed** — no longer needed without FlatCompat
83
+ - **`eslint-config-prettier` restored** — required by `eslint-plugin-prettier/recommended`
84
+ - **`@eslint/js` restored** — used for the JavaScript recommended rule set
84
85
  - **`plugins` option now accepts flat config arrays/objects** — no string-based plugin names
85
86
 
86
87
  ### Required updates
87
88
 
88
89
  1. Install ESLint 10+: `bun add --dev eslint@10`
89
90
  2. Replace `eslint-plugin-import` with `eslint-plugin-import-x`
90
- 3. Remove unused deps: `@eslint/eslintrc`, `@eslint/js`
91
+ 3. Remove unused deps: `@eslint/eslintrc`, `eslint-plugin-import`
91
92
 
92
93
  ### What's new in v2.0.4
93
94
 
@@ -96,6 +97,13 @@ v2 drops FlatCompat and uses native ESLint 10 flat configs throughout.
96
97
  - **All packages declared as peer deps** — every config module's dependencies are declared with proper `peerDependenciesMeta`
97
98
  - **Flat config resolver fix** — switched from string-based `import-x/resolver` to `import-x/resolver-next` with `createTypeScriptImportResolver` for ESLint 10 compatibility
98
99
 
100
+ ### What's new in v2.1.0
101
+
102
+ - **Root import no longer loads optional ESLint integrations** — use ESLint subpath imports for TypeScript, React, and Next.js presets.
103
+ - **JavaScript recommended rules restored** — the base JavaScript preset now includes `@eslint/js` recommended rules.
104
+ - **Next.js peer dependency declared** — the Next.js preset requires `next` because `eslint-config-next` resolves Next's bundled ESLint parser.
105
+ - **Custom file patterns honored consistently** — TypeScript, React, and Next.js presets merge `options.files` into their generated file patterns.
106
+
99
107
  ## Quick Start
100
108
 
101
109
  ### ESLint
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vijayhardaha/dev-config",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "Reusable development configurations for Next.js + TypeScript projects",
5
5
  "scripts": {
6
6
  "lint": "eslint .",
@@ -65,28 +65,30 @@
65
65
  "typescript"
66
66
  ],
67
67
  "devDependencies": {
68
+ "@eslint/js": "^10.0.1",
68
69
  "@commitlint/cli": "^21.0.2",
69
70
  "@commitlint/config-conventional": "^21.0.2",
70
71
  "@commitlint/types": "^21.0.1",
71
- "@typescript-eslint/parser": "^8.60.1",
72
- "@typescript-eslint/eslint-plugin": "^8.60.1",
72
+ "@typescript-eslint/parser": "^8.61.0",
73
+ "@typescript-eslint/eslint-plugin": "^8.61.0",
73
74
  "@vitest/coverage-v8": "^4.1.8",
74
75
  "eslint": "^10.4.1",
75
- "eslint-config-next": "^16.2.7",
76
+ "eslint-config-next": "^16.2.9",
76
77
  "eslint-config-prettier": "^10.1.8",
77
78
  "eslint-plugin-import-x": "^4.16.2",
78
- "eslint-plugin-jsdoc": "^63.0.1",
79
+ "eslint-plugin-jsdoc": "^63.0.2",
79
80
  "eslint-plugin-jsx-a11y": "^6.10.2",
80
81
  "eslint-plugin-prettier": "^5.5.6",
81
82
  "eslint-plugin-react": "^7.37.5",
82
83
  "eslint-plugin-react-hooks": "^7.1.1",
83
84
  "globals": "^17.6.0",
84
85
  "husky": "^9.1.7",
85
- "prettier": "^3.8.3",
86
+ "next": "^16.2.9",
87
+ "prettier": "^3.8.4",
86
88
  "@prettier/plugin-xml": "^3.4.2",
87
89
  "release-it": "^20.2.0",
88
90
  "typescript": "^6.0.3",
89
- "typescript-eslint": "^8.60.1",
91
+ "typescript-eslint": "^8.61.0",
90
92
  "vitest": "^4.1.8"
91
93
  },
92
94
  "peerDependencies": {
@@ -94,6 +96,7 @@
94
96
  "@commitlint/config-conventional": ">=21",
95
97
  "@commitlint/types": ">=21",
96
98
  "@eslint/compat": ">=2",
99
+ "@eslint/js": ">=10",
97
100
  "@next/eslint-plugin-next": ">=16",
98
101
  "@prettier/plugin-xml": ">=3",
99
102
  "@typescript-eslint/eslint-plugin": ">=8",
@@ -111,6 +114,7 @@
111
114
  "globals": ">=17",
112
115
  "husky": ">=9",
113
116
  "next-sitemap": ">=4",
117
+ "next": ">=16",
114
118
  "prettier": ">=3",
115
119
  "stylelint": ">=17",
116
120
  "stylelint-config-property-sort-order-smacss": ">=11",
@@ -159,6 +163,9 @@
159
163
  "next-sitemap": {
160
164
  "optional": true
161
165
  },
166
+ "next": {
167
+ "optional": true
168
+ },
162
169
  "stylelint": {
163
170
  "optional": true
164
171
  },
@@ -9,6 +9,8 @@
9
9
  * =====================================================================
10
10
  */
11
11
 
12
+ import js from '@eslint/js';
13
+
12
14
  import { buildConfig, files } from './lib/index.js';
13
15
 
14
16
  /**
@@ -34,6 +36,7 @@ export const createConfig = (options = {}) => {
34
36
 
35
37
  return buildConfig({
36
38
  files: [...files.withoutTs, ...(options.files || [])],
39
+ builtinPlugins: [js.configs.recommended],
37
40
  options: { ...options, prettier, importOrder, jsdoc },
38
41
  });
39
42
  };
@@ -1,3 +1,4 @@
1
+ import { ESLint } from 'eslint';
1
2
  import { describe, it, expect } from 'vitest';
2
3
 
3
4
  // Test suite for the ESLint module's main entry point.
@@ -19,4 +20,17 @@ describe('eslint/index.js', () => {
19
20
  // Verify that the default export is defined (should be a base ESLint config object).
20
21
  expect(module.default).toBeDefined();
21
22
  });
23
+
24
+ // Test that the JavaScript config catches basic correctness issues.
25
+ it('should include recommended JavaScript rules', async () => {
26
+ const module = await import('./index.js');
27
+ const eslint = new ESLint({
28
+ overrideConfigFile: true,
29
+ overrideConfig: module.createConfig({ prettier: false, importOrder: false, jsdoc: false }),
30
+ });
31
+
32
+ const [result] = await eslint.lintText('const value = missing + 1;\n', { filePath: 'sample.js' });
33
+
34
+ expect(result.messages.some((message) => message.ruleId === 'no-undef')).toBe(true);
35
+ });
22
36
  });
@@ -97,7 +97,8 @@ const stripPlugins = (flatConfigs, pluginNames) => {
97
97
  }
98
98
 
99
99
  if (Object.keys(plugins).length === 0) {
100
- const { plugins: _, ...rest } = config;
100
+ const rest = { ...config };
101
+ delete rest.plugins;
101
102
 
102
103
  return rest;
103
104
  }
@@ -118,7 +119,8 @@ const stripParser = (flatConfigs) =>
118
119
  flatConfigs.map((config) => {
119
120
  if (!config.languageOptions?.parser) return config;
120
121
 
121
- const { parser: _, ...languageOptions } = config.languageOptions;
122
+ const languageOptions = { ...config.languageOptions };
123
+ delete languageOptions.parser;
122
124
 
123
125
  return Object.keys(languageOptions).length > 0
124
126
  ? { ...config, languageOptions }
@@ -161,7 +163,7 @@ const buildConfigObject = ({
161
163
  extraSettings,
162
164
  extraRules,
163
165
  }) => {
164
- const { ignores, rules, settings, languageOptions, extend } = opts;
166
+ const { ignores, rules, settings, extend } = opts;
165
167
 
166
168
  return {
167
169
  files: [...filePatterns],
@@ -54,7 +54,7 @@ export const createConfig = (options = {}) => {
54
54
  const { prettier = true, react = true, a11y = true, importOrder = true, jsdoc = true } = options;
55
55
 
56
56
  return buildConfig({
57
- files: files.withTs,
57
+ files: [...files.withTs, ...(options.files || [])],
58
58
  builtinPlugins: [...prepareNextConfig(nextCoreWebVitals)],
59
59
  centralPlugins: { react: reactPlugin, 'react-hooks': reactHooks, '@typescript-eslint': tsEslint.plugin },
60
60
  parserOptions: { ecmaFeatures: { jsx: true } },
@@ -4,29 +4,28 @@ import { describe, it, expect } from 'vitest';
4
4
  describe('eslint/next.js', () => {
5
5
  // Test that the module exports the createConfig function.
6
6
  it('should export createConfig function', async () => {
7
- try {
8
- // Dynamically import the next.js module to test its exports.
9
- const module = await import('./next.js');
7
+ // Dynamically import the next.js module to test its exports.
8
+ const module = await import('./next.js');
10
9
 
11
- // Verify that createConfig is a function (used to create ESLint config for Next.js).
12
- expect(typeof module.createConfig).toBe('function');
13
- } catch (error) {
14
- // If import fails, expect an error to be defined.
15
- expect(error).toBeDefined();
16
- }
10
+ // Verify that createConfig is a function (used to create ESLint config for Next.js).
11
+ expect(typeof module.createConfig).toBe('function');
17
12
  });
18
13
 
19
14
  // Test that the module exports a default configuration object.
20
15
  it('should export default config', async () => {
21
- try {
22
- // Dynamically import the next.js module to test its exports.
23
- const module = await import('./next.js');
16
+ // Dynamically import the next.js module to test its exports.
17
+ const module = await import('./next.js');
24
18
 
25
- // Verify that the default export is defined (should be an ESLint config object).
26
- expect(module.default).toBeDefined();
27
- } catch (error) {
28
- // If import fails, expect an error to be defined.
29
- expect(error).toBeDefined();
30
- }
19
+ // Verify that the default export is defined (should be an ESLint config object).
20
+ expect(module.default).toBeDefined();
21
+ });
22
+
23
+ // Test that additional file patterns are applied to the generated config.
24
+ it('should include additional file patterns from options', async () => {
25
+ const module = await import('./next.js');
26
+ const result = module.createConfig({ files: ['custom/**/*.tsx'] });
27
+ const configObject = result.at(-1);
28
+
29
+ expect(configObject.files).toContain('custom/**/*.tsx');
31
30
  });
32
31
  });
@@ -41,14 +41,16 @@ export const createConfig = (options = {}) => {
41
41
  const { prettier = true, a11y = true, importOrder = true, jsdoc = true } = options;
42
42
 
43
43
  return buildConfig({
44
- files: files.withTs,
44
+ files: [...files.withTs, ...(options.files || [])],
45
45
  builtinPlugins: [
46
- { ...reactRecommended, files: files.withTs },
47
- { ...reactHooks.configs.flat.recommended, files: files.withTs },
46
+ { ...reactRecommended, files: [...files.withTs, ...(options.files || [])] },
47
+ { ...reactHooks.configs.flat.recommended, files: [...files.withTs, ...(options.files || [])] },
48
48
  ...tsEslint.configs.recommended,
49
49
  ],
50
50
  centralPlugins: { react: reactPlugin, 'react-hooks': reactHooks, '@typescript-eslint': tsEslint.plugin },
51
- conditionalPlugins: { a11y: { ...jsxA11y.flatConfigs.recommended, files: files.withTs } },
51
+ conditionalPlugins: {
52
+ a11y: { ...jsxA11y.flatConfigs.recommended, files: [...files.withTs, ...(options.files || [])] },
53
+ },
52
54
  parserOptions: { ecmaFeatures: { jsx: true } },
53
55
  settings: { react: { version: 'detect' } },
54
56
  rules: { 'react/react-in-jsx-scope': 'off', 'react/no-unknown-property': ['error', { ignore: ['jsx', 'global'] }] },
@@ -19,4 +19,13 @@ describe('eslint/react.js', () => {
19
19
  // Verify that the default export is defined (should be an ESLint config object).
20
20
  expect(module.default).toBeDefined();
21
21
  });
22
+
23
+ // Test that additional file patterns are applied to the generated config.
24
+ it('should include additional file patterns from options', async () => {
25
+ const module = await import('./react.js');
26
+ const result = module.createConfig({ files: ['custom/**/*.tsx'] });
27
+ const configObject = result.at(-1);
28
+
29
+ expect(configObject.files).toContain('custom/**/*.tsx');
30
+ });
22
31
  });
@@ -35,7 +35,7 @@ export const createConfig = (options = {}) => {
35
35
  const { prettier = true, importOrder = true, jsdoc = true } = options;
36
36
 
37
37
  return buildConfig({
38
- files: files.withTs,
38
+ files: [...files.withTs, ...(options.files || [])],
39
39
  builtinPlugins: [...tsEslint.configs.recommended],
40
40
  centralPlugins: { '@typescript-eslint': tsEslint.plugin },
41
41
  typescript: true,
@@ -19,4 +19,13 @@ describe('eslint/typescript.js', () => {
19
19
  // Verify that the default export is defined (should be an ESLint config object).
20
20
  expect(module.default).toBeDefined();
21
21
  });
22
+
23
+ // Test that additional file patterns are applied to the generated config.
24
+ it('should include additional file patterns from options', async () => {
25
+ const module = await import('./typescript.js');
26
+ const result = module.createConfig({ files: ['custom/**/*.ts'] });
27
+ const configObject = result.at(-1);
28
+
29
+ expect(configObject.files).toContain('custom/**/*.ts');
30
+ });
22
31
  });
package/src/index.js CHANGED
@@ -15,27 +15,6 @@
15
15
  */
16
16
  export { default as eslint, createConfig as createEslintConfig } from './eslint/index.js';
17
17
 
18
- /**
19
- * ESLint configuration for TypeScript files.
20
- *
21
- * @type {import('eslint').Linter.Config}
22
- */
23
- export { default as eslintTs, createConfig as createEslintTsConfig } from './eslint/typescript.js';
24
-
25
- /**
26
- * ESLint configuration for React files.
27
- *
28
- * @type {import('eslint').Linter.Config}
29
- */
30
- export { default as eslintReact, createConfig as createEslintReactConfig } from './eslint/react.js';
31
-
32
- /**
33
- * ESLint configuration for Next.js files.
34
- *
35
- * @type {import('eslint').Linter.Config}
36
- */
37
- export { default as eslintNext, createConfig as createEslintNextConfig } from './eslint/next.js';
38
-
39
18
  /**
40
19
  * Prettier configuration.
41
20
  *
package/src/index.test.js CHANGED
@@ -3,34 +3,23 @@ import { describe, it, expect } from 'vitest';
3
3
  // Test suite for the main index.js exports.
4
4
  describe('index.js exports', () => {
5
5
  // Test that all expected functions and objects are exported from the module.
6
- it('should export functions', async () => {
7
- try {
8
- // Dynamically import the module to test its exports.
9
- const module = await import('./index.js');
6
+ it('should export functions without requiring optional integrations', async () => {
7
+ // Dynamically import the module to test its exports.
8
+ const module = await import('./index.js');
10
9
 
11
- // Verify ESLint-related exports are functions.
12
- expect(typeof module.eslint).toBe('function');
13
- expect(typeof module.createEslintConfig).toBe('function');
14
- expect(typeof module.eslintTs).toBe('function');
15
- expect(typeof module.createEslintTsConfig).toBe('function');
16
- expect(typeof module.eslintReact).toBe('function');
17
- expect(typeof module.createEslintReactConfig).toBe('function');
18
- expect(typeof module.eslintNext).toBe('function');
19
- expect(typeof module.createEslintNextConfig).toBe('function');
10
+ // Verify base ESLint exports are available without optional integrations.
11
+ expect(Array.isArray(module.eslint)).toBe(true);
12
+ expect(typeof module.createEslintConfig).toBe('function');
20
13
 
21
- // Verify config objects are exported.
22
- expect(typeof module.prettier).toBe('object');
23
- expect(typeof module.commitlint).toBe('object');
24
- expect(typeof module.nextSitemap).toBe('object');
14
+ // Verify config objects are exported.
15
+ expect(typeof module.prettier).toBe('object');
16
+ expect(typeof module.commitlint).toBe('object');
17
+ expect(typeof module.nextSitemap).toBe('object');
25
18
 
26
- // Verify sitemap config function is exported.
27
- expect(typeof module.createSitemapConfig).toBe('function');
19
+ // Verify sitemap config function is exported.
20
+ expect(typeof module.createSitemapConfig).toBe('function');
28
21
 
29
- // Verify Stylelint config object is exported.
30
- expect(typeof module.stylelint).toBe('object');
31
- } catch (error) {
32
- // If import fails, expect an error to be defined.
33
- expect(error).toBeDefined();
34
- }
22
+ // Verify Stylelint config object is exported.
23
+ expect(typeof module.stylelint).toBe('object');
35
24
  });
36
25
  });
@@ -71,7 +71,10 @@ const config = {
71
71
 
72
72
  // ---- XML ----
73
73
  // Use 2-space indentation for XML files
74
- { files: ['.xml', '.xsd', '.xsl', '.xslt'], options: { tabWidth: 2 } },
74
+ {
75
+ files: ['**/*.xml', '**/*.xsd', '**/*.xsl', '**/*.xslt'],
76
+ options: { tabWidth: 2, xmlWhitespaceSensitivity: 'preserve' },
77
+ },
75
78
  ],
76
79
  };
77
80
 
@@ -36,4 +36,12 @@ describe('prettier/index.js', () => {
36
36
  expect(Array.isArray(config.overrides)).toBe(true);
37
37
  expect(config.overrides.length).toBeGreaterThan(0);
38
38
  });
39
+
40
+ // Test that XML-like extensions are matched as normal file globs.
41
+ it('should use XML glob patterns that match normal files', async () => {
42
+ const module = await import('./index.js');
43
+ const xmlOverride = module.default.overrides.find((override) => override.options?.xmlWhitespaceSensitivity);
44
+
45
+ expect(xmlOverride.files).toEqual(['**/*.xml', '**/*.xsd', '**/*.xsl', '**/*.xslt']);
46
+ });
39
47
  });