@puruslang/linter 0.1.0 → 0.2.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/package.json +1 -1
- package/src/cli.js +85 -13
- package/src/config.js +67 -0
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -4,16 +4,21 @@
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const { lint, defaultRules } = require("./index.js");
|
|
7
|
+
const { loadConfig } = require("./config.js");
|
|
7
8
|
|
|
8
9
|
const args = process.argv.slice(2);
|
|
9
10
|
|
|
10
|
-
if (args.
|
|
11
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
11
12
|
console.log("purus-lint - Linter for the Purus language");
|
|
12
13
|
console.log("");
|
|
13
|
-
console.log("Usage:
|
|
14
|
+
console.log("Usage:");
|
|
15
|
+
console.log(" purus-lint [file...] Lint specific files");
|
|
16
|
+
console.log(" purus-lint --directory <dir> Lint all files in directory");
|
|
17
|
+
console.log(" purus-lint Lint using config.purus");
|
|
14
18
|
console.log("");
|
|
15
19
|
console.log("Options:");
|
|
16
20
|
console.log(" --config <file> Path to config JSON file");
|
|
21
|
+
console.log(" --directory, -d Directory to lint");
|
|
17
22
|
console.log(" --fix (not yet implemented)");
|
|
18
23
|
console.log(" --help Show this help");
|
|
19
24
|
process.exit(0);
|
|
@@ -21,18 +26,22 @@ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
|
21
26
|
|
|
22
27
|
// Collect files and options
|
|
23
28
|
let configPath = null;
|
|
29
|
+
let directory = null;
|
|
24
30
|
const files = [];
|
|
25
31
|
|
|
26
32
|
for (let i = 0; i < args.length; i++) {
|
|
27
33
|
if (args[i] === "--config" && i + 1 < args.length) {
|
|
28
34
|
configPath = args[++i];
|
|
35
|
+
} else if ((args[i] === "--directory" || args[i] === "-d") && i + 1 < args.length) {
|
|
36
|
+
directory = args[++i];
|
|
29
37
|
} else if (!args[i].startsWith("-")) {
|
|
30
38
|
files.push(args[i]);
|
|
31
39
|
}
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
// Load
|
|
42
|
+
// Load rule overrides
|
|
35
43
|
let ruleOverrides = {};
|
|
44
|
+
|
|
36
45
|
if (configPath) {
|
|
37
46
|
try {
|
|
38
47
|
ruleOverrides = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
@@ -40,23 +49,72 @@ if (configPath) {
|
|
|
40
49
|
console.error(`Error reading config: ${err.message}`);
|
|
41
50
|
process.exit(1);
|
|
42
51
|
}
|
|
52
|
+
} else {
|
|
53
|
+
// Try config.purus for lint settings
|
|
54
|
+
const result = loadConfig();
|
|
55
|
+
if (result && result.config.lint) {
|
|
56
|
+
ruleOverrides = {};
|
|
57
|
+
const lintConfig = result.config.lint;
|
|
58
|
+
for (const [key, value] of Object.entries(lintConfig)) {
|
|
59
|
+
if (typeof value === "string") {
|
|
60
|
+
ruleOverrides[key] = { severity: value };
|
|
61
|
+
} else if (typeof value === "number") {
|
|
62
|
+
// For numeric values like indent-size, set as the relevant property
|
|
63
|
+
if (key === "indent-size") {
|
|
64
|
+
ruleOverrides[key] = { severity: "warn", size: value };
|
|
65
|
+
} else if (key === "max-line-length") {
|
|
66
|
+
ruleOverrides[key] = { severity: "warn", max: value };
|
|
67
|
+
}
|
|
68
|
+
} else if (typeof value === "object") {
|
|
69
|
+
ruleOverrides[key] = value;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Also check for .puruslint.json in cwd
|
|
75
|
+
if (Object.keys(ruleOverrides).length === 0) {
|
|
76
|
+
const defaultConfig = path.join(process.cwd(), ".puruslint.json");
|
|
77
|
+
if (fs.existsSync(defaultConfig)) {
|
|
78
|
+
try {
|
|
79
|
+
ruleOverrides = JSON.parse(fs.readFileSync(defaultConfig, "utf8"));
|
|
80
|
+
} catch {
|
|
81
|
+
// ignore
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Determine files to lint
|
|
88
|
+
let filesToLint = files;
|
|
89
|
+
|
|
90
|
+
if (filesToLint.length === 0 && directory) {
|
|
91
|
+
filesToLint = findPurusFiles(path.resolve(directory));
|
|
43
92
|
}
|
|
44
93
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
// ignore
|
|
94
|
+
if (filesToLint.length === 0) {
|
|
95
|
+
// Try config.purus entry
|
|
96
|
+
const result = loadConfig();
|
|
97
|
+
if (result) {
|
|
98
|
+
const entryDir = path.resolve(result.configDir, result.config.entry || "src");
|
|
99
|
+
if (fs.existsSync(entryDir)) {
|
|
100
|
+
filesToLint = findPurusFiles(entryDir);
|
|
53
101
|
}
|
|
54
102
|
}
|
|
55
103
|
}
|
|
56
104
|
|
|
105
|
+
if (filesToLint.length === 0) {
|
|
106
|
+
console.log("purus-lint - Linter for the Purus language");
|
|
107
|
+
console.log("");
|
|
108
|
+
console.log("Usage:");
|
|
109
|
+
console.log(" purus-lint [file...] Lint specific files");
|
|
110
|
+
console.log(" purus-lint --directory <dir> Lint all files in directory");
|
|
111
|
+
console.log(" purus-lint Lint using config.purus");
|
|
112
|
+
process.exit(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
57
115
|
let totalIssues = 0;
|
|
58
116
|
|
|
59
|
-
for (const file of
|
|
117
|
+
for (const file of filesToLint) {
|
|
60
118
|
let source;
|
|
61
119
|
try {
|
|
62
120
|
source = fs.readFileSync(file, "utf8");
|
|
@@ -77,6 +135,20 @@ for (const file of files) {
|
|
|
77
135
|
if (totalIssues > 0) {
|
|
78
136
|
console.log(`\n${totalIssues} issue${totalIssues === 1 ? "" : "s"} found.`);
|
|
79
137
|
process.exit(1);
|
|
80
|
-
} else if (
|
|
138
|
+
} else if (filesToLint.length > 0) {
|
|
81
139
|
console.log("No issues found.");
|
|
82
140
|
}
|
|
141
|
+
|
|
142
|
+
function findPurusFiles(dir) {
|
|
143
|
+
const results = [];
|
|
144
|
+
if (!fs.existsSync(dir)) return results;
|
|
145
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
146
|
+
const fullPath = path.join(dir, entry.name);
|
|
147
|
+
if (entry.isDirectory()) {
|
|
148
|
+
results.push(...findPurusFiles(fullPath));
|
|
149
|
+
} else if (/\.(c|m)?purus$/.test(entry.name) && entry.name !== "config.purus") {
|
|
150
|
+
results.push(fullPath);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
function parseConfig(configPath) {
|
|
7
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
8
|
+
const config = {};
|
|
9
|
+
|
|
10
|
+
for (const line of content.split("\n")) {
|
|
11
|
+
const trimmed = line.trim();
|
|
12
|
+
if (trimmed === "" || trimmed.startsWith("--")) continue;
|
|
13
|
+
|
|
14
|
+
const match = trimmed.match(/^const\s+([\w.-]+)\s+be\s+(.+)$/);
|
|
15
|
+
if (!match) continue;
|
|
16
|
+
|
|
17
|
+
const key = match[1];
|
|
18
|
+
let value = match[2].trim();
|
|
19
|
+
|
|
20
|
+
if (value.startsWith("///") && value.endsWith("///")) {
|
|
21
|
+
value = value.slice(3, -3);
|
|
22
|
+
} else if (value === "true") {
|
|
23
|
+
value = true;
|
|
24
|
+
} else if (value === "false") {
|
|
25
|
+
value = false;
|
|
26
|
+
} else if (/^\d+$/.test(value)) {
|
|
27
|
+
value = parseInt(value, 10);
|
|
28
|
+
} else if (/^\d+\.\d+$/.test(value)) {
|
|
29
|
+
value = parseFloat(value);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const keys = key.split(".");
|
|
33
|
+
let target = config;
|
|
34
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
35
|
+
if (!target[keys[i]] || typeof target[keys[i]] !== "object") {
|
|
36
|
+
target[keys[i]] = {};
|
|
37
|
+
}
|
|
38
|
+
target = target[keys[i]];
|
|
39
|
+
}
|
|
40
|
+
target[keys[keys.length - 1]] = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return config;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function findConfig(startDir) {
|
|
47
|
+
let dir = startDir || process.cwd();
|
|
48
|
+
while (true) {
|
|
49
|
+
const configPath = path.join(dir, "config.purus");
|
|
50
|
+
if (fs.existsSync(configPath)) return configPath;
|
|
51
|
+
const parent = path.dirname(dir);
|
|
52
|
+
if (parent === dir) return null;
|
|
53
|
+
dir = parent;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function loadConfig(startDir) {
|
|
58
|
+
const configPath = findConfig(startDir);
|
|
59
|
+
if (!configPath) return null;
|
|
60
|
+
return {
|
|
61
|
+
config: parseConfig(configPath),
|
|
62
|
+
configPath,
|
|
63
|
+
configDir: path.dirname(configPath),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { parseConfig, findConfig, loadConfig };
|