eslint-plugin-package-jsonc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 otakutyrant
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # eslint-plugin-package-jsonc
2
+
3
+ An ESLint plugin that ensures `package.json` is consistent with `package.jsonc`.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install --save-dev eslint-plugin-package-jsonc
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Add the plugin to your ESLint configuration:
14
+
15
+ ### Flat Config (eslint.config.js)
16
+
17
+ ```javascript
18
+ import packageJsonc from "eslint-plugin-package-jsonc";
19
+
20
+ export default [
21
+ {
22
+ plugins: {
23
+ "package-jsonc": packageJsonc,
24
+ },
25
+ rules: {
26
+ "package-jsonc/sync": "error",
27
+ },
28
+ },
29
+ ];
30
+ ```
31
+
32
+ ### Legacy Config (.eslintrc)
33
+
34
+ ```json
35
+ {
36
+ "plugins": ["package-jsonc"],
37
+ "rules": {
38
+ "package-jsonc/sync": "error"
39
+ }
40
+ }
41
+ ```
42
+
43
+ ## Rule: `package-jsonc/sync`
44
+
45
+ This rule ensures that `package.json` is always in sync with `package.jsonc`:
46
+
47
+ - If `package.json` does not exist but `package.jsonc` does, the rule reports an error and can generate `package.json` when running with `--fix`.
48
+ - If `package.json` exists but has different content than what would be generated from `package.jsonc`, the rule reports an error and can update `package.json` when running with `--fix`.
49
+
50
+ ### Fix Mode
51
+
52
+ When running ESLint with the `--fix` flag, the plugin will automatically:
53
+
54
+ 1. Generate `package.json` if it doesn't exist
55
+ 2. Update `package.json` if it's inconsistent with `package.jsonc`
56
+
57
+ ```bash
58
+ eslint package.jsonc --fix
59
+ ```
60
+
61
+ ## How It Works
62
+
63
+ 1. The plugin parses `package.jsonc` (JSON with comments) by stripping out comments
64
+ 2. It compares the parsed content with the existing `package.json` (if any)
65
+ 3. If they differ, it reports an error
66
+ 4. In fix mode, it writes the normalized JSON to `package.json`
67
+
68
+ ## Why Use This?
69
+
70
+ - **Comments in package.json**: JSONC allows you to add comments to your package configuration while maintaining a standard `package.json` for npm compatibility.
71
+ - **Single source of truth**: `package.jsonc` becomes the source of truth, and `package.json` is auto-generated.
@@ -0,0 +1,4 @@
1
+ import type { ESLint } from "eslint";
2
+ declare const plugin: ESLint.Plugin;
3
+ export default plugin;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,MAYpB,CAAC;AAMF,eAAe,MAAM,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ import packageJsoncRule, { clearFixedFiles } from "./rules/package-jsonc.js";
2
+ const plugin = {
3
+ rules: {
4
+ sync: packageJsoncRule,
5
+ },
6
+ configs: {
7
+ recommended: {
8
+ plugins: ["package-jsonc"],
9
+ rules: {
10
+ "package-jsonc/sync": "error",
11
+ },
12
+ },
13
+ },
14
+ };
15
+ // Export the clearFixedFiles function for testing
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ plugin.clearFixedFiles = clearFixedFiles;
18
+ export default plugin;
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,gBAAgB,EAAE,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE7E,MAAM,MAAM,GAAkB;IAC1B,KAAK,EAAE;QACH,IAAI,EAAE,gBAAgB;KACzB;IACD,OAAO,EAAE;QACL,WAAW,EAAE;YACT,OAAO,EAAE,CAAC,eAAe,CAAC;YAC1B,KAAK,EAAE;gBACH,oBAAoB,EAAE,OAAO;aAChC;SACJ;KACJ;CACJ,CAAC;AAEF,kDAAkD;AAClD,8DAA8D;AAC7D,MAAc,CAAC,eAAe,GAAG,eAAe,CAAC;AAElD,eAAe,MAAM,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Rule } from "eslint";
2
+ declare const rule: Rule.RuleModule;
3
+ export declare function clearFixedFiles(): void;
4
+ export default rule;
5
+ //# sourceMappingURL=package-jsonc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-jsonc.d.ts","sourceRoot":"","sources":["../../src/rules/package-jsonc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAkDnC,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,UAkGhB,CAAC;AAGF,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED,eAAe,IAAI,CAAC"}
@@ -0,0 +1,127 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ // Track which files have been fixed in this ESLint run to avoid circular fixes
4
+ const fixedFiles = new Set();
5
+ /**
6
+ * Parse JSONC content (JSON with Comments) into a JavaScript object.
7
+ * Removes single-line and multi-line comments.
8
+ * @param content - The JSONC content
9
+ * @returns The parsed JSON object
10
+ */
11
+ function parseJSONC(content) {
12
+ // Remove single-line comments (// ...)
13
+ let cleaned = content.replace(/\/\/.*$/gm, "");
14
+ // Remove multi-line comments (/* ... */)
15
+ cleaned = cleaned.replace(/\/\*[\s\S]*?\*\//g, "");
16
+ // Parse the cleaned content as JSON
17
+ return JSON.parse(cleaned);
18
+ }
19
+ /**
20
+ * Get the text representation of a value for comparison.
21
+ * Normalizes JSON formatting for reliable comparison.
22
+ * @param object - The object to stringify
23
+ * @returns Normalized JSON string
24
+ */
25
+ function getNormalizedJson(object) {
26
+ return JSON.stringify(object, null, 2) + "\n";
27
+ }
28
+ /**
29
+ * Check if package.json needs to be updated.
30
+ * This is called during the fix phase to avoid circular fixes.
31
+ */
32
+ function needsFix(packageJsonPath, normalizedJsonc) {
33
+ try {
34
+ const content = fs.readFileSync(packageJsonPath, "utf8");
35
+ const data = JSON.parse(content);
36
+ return normalizedJsonc !== getNormalizedJson(data);
37
+ }
38
+ catch {
39
+ // File doesn't exist or is invalid
40
+ return true;
41
+ }
42
+ }
43
+ const rule = {
44
+ meta: {
45
+ type: "problem",
46
+ docs: {
47
+ description: "Ensure package.json is consistent with package.jsonc",
48
+ category: "Possible Errors",
49
+ recommended: true,
50
+ },
51
+ fixable: "code",
52
+ schema: [],
53
+ messages: {
54
+ missingPackageJson: "package.json does not exist but package.jsonc does. Run 'npx eslint package.jsonc --fix' to generate it.",
55
+ inconsistentPackageJson: "package.json is inconsistent with package.jsonc. Run 'npx eslint package.jsonc --fix' to update it.",
56
+ },
57
+ },
58
+ create(context) {
59
+ const filename = context.getFilename();
60
+ const basename = path.basename(filename);
61
+ // Only process package.jsonc files
62
+ if (basename !== "package.jsonc") {
63
+ return {};
64
+ }
65
+ const directory = path.dirname(filename);
66
+ const packageJsonPath = path.join(directory, "package.json");
67
+ return {
68
+ Program(node) {
69
+ const sourceCode = context.getSourceCode();
70
+ const jsoncContent = sourceCode.getText();
71
+ let jsoncData;
72
+ try {
73
+ jsoncData = parseJSONC(jsoncContent);
74
+ }
75
+ catch {
76
+ // If package.jsonc has syntax errors, let other rules handle it
77
+ return;
78
+ }
79
+ const normalizedJsonc = getNormalizedJson(jsoncData);
80
+ // Check if package.json exists and is consistent
81
+ let isConsistent = false;
82
+ try {
83
+ const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
84
+ const packageJsonData = JSON.parse(packageJsonContent);
85
+ isConsistent = normalizedJsonc === getNormalizedJson(packageJsonData);
86
+ }
87
+ catch {
88
+ // package.json does not exist or is invalid
89
+ }
90
+ if (isConsistent) {
91
+ return;
92
+ }
93
+ // Report the error with a fixer
94
+ const messageId = fs.existsSync(packageJsonPath)
95
+ ? "inconsistentPackageJson"
96
+ : "missingPackageJson";
97
+ context.report({
98
+ node,
99
+ messageId,
100
+ fix(fixer) {
101
+ // Check if we've already fixed this file in this run
102
+ if (fixedFiles.has(filename)) {
103
+ return null;
104
+ }
105
+ // Check if fix is actually needed
106
+ if (!needsFix(packageJsonPath, normalizedJsonc)) {
107
+ fixedFiles.add(filename);
108
+ return null;
109
+ }
110
+ // Apply the fix
111
+ fixedFiles.add(filename);
112
+ fs.writeFileSync(packageJsonPath, normalizedJsonc, "utf8");
113
+ // Return null to indicate no source text changes
114
+ // The actual fix happened to a different file (package.json)
115
+ return null;
116
+ },
117
+ });
118
+ },
119
+ };
120
+ },
121
+ };
122
+ // Export a function to clear the fixed files set (for testing)
123
+ export function clearFixedFiles() {
124
+ fixedFiles.clear();
125
+ }
126
+ export default rule;
127
+ //# sourceMappingURL=package-jsonc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-jsonc.js","sourceRoot":"","sources":["../../src/rules/package-jsonc.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "eslint-plugin-package-jsonc",
3
+ "version": "1.0.0",
4
+ "description": "ESLint plugin to ensure package.json is consistent with package.jsonc",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "keywords": [
20
+ "eslint",
21
+ "eslintplugin",
22
+ "eslint-plugin",
23
+ "package.json",
24
+ "package.jsonc",
25
+ "jsonc"
26
+ ],
27
+ "peerDependencies": {
28
+ "eslint": ">=8.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@eslint/compat": "^1.4.0",
32
+ "@eslint/js": "^9.36.0",
33
+ "@types/eslint": "^9.6.1",
34
+ "@types/node": "^25.2.1",
35
+ "eslint": "^9.39.1",
36
+ "espree": "^11.1.0",
37
+ "globals": "^17.3.0",
38
+ "jiti": "^2.6.1",
39
+ "prettier": "^3.6.2",
40
+ "prettier-plugin-organize-imports": "^4.2.0",
41
+ "typescript": "^5",
42
+ "typescript-eslint": "^8.48.0"
43
+ },
44
+ "license": "MIT",
45
+ "scripts": {
46
+ "build": "tsc",
47
+ "lint": "tsc --noEmit && eslint",
48
+ "format": "prettier --write . --log-level warn",
49
+ "test": "node test/package-jsonc.test.js"
50
+ }
51
+ }