@puruslang/linter 0.0.3 → 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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +85 -13
  3. package/src/config.js +67 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@puruslang/linter",
3
- "version": "0.0.3",
3
+ "version": "0.2.0",
4
4
  "description": "Linter for the Purus language",
5
5
  "license": "Apache-2.0",
6
6
  "main": "src/index.js",
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.length === 0 || args.includes("--help") || args.includes("-h")) {
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: purus-lint <file.purus|.cpurus|.mpurus> [options]");
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 config
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
- // Also check for .puruslint.json in cwd
46
- if (!configPath) {
47
- const defaultConfig = path.join(process.cwd(), ".puruslint.json");
48
- if (fs.existsSync(defaultConfig)) {
49
- try {
50
- ruleOverrides = JSON.parse(fs.readFileSync(defaultConfig, "utf8"));
51
- } catch {
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 files) {
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 (files.length > 0) {
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 };