@atlisp/lint 0.2.16 → 0.2.18

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 (80) hide show
  1. package/README.md +1 -1
  2. package/atlisp-lint.default.json +1 -1
  3. package/dist/checks/arg-count.d.ts +2 -1
  4. package/dist/checks/arg-count.js +37 -19
  5. package/dist/checks/builtin-arg-count.d.ts +2 -1
  6. package/dist/checks/builtin-arg-count.js +3 -2
  7. package/dist/checks/cond-duplicate.js +5 -6
  8. package/dist/checks/constant-condition.js +9 -9
  9. package/dist/checks/dangerous-calls.js +1 -1
  10. package/dist/checks/dangling-defun.d.ts +0 -7
  11. package/dist/checks/dangling-defun.js +0 -63
  12. package/dist/checks/double-not.js +1 -1
  13. package/dist/checks/empty-branch.js +1 -1
  14. package/dist/checks/empty-catch.d.ts +2 -1
  15. package/dist/checks/empty-catch.js +8 -7
  16. package/dist/checks/function-complexity.js +5 -18
  17. package/dist/checks/global-naming.js +1 -1
  18. package/dist/checks/identical-branches.js +3 -3
  19. package/dist/checks/if-without-else.js +1 -1
  20. package/dist/checks/misplaced-else.js +2 -2
  21. package/dist/checks/missing-export.js +3 -3
  22. package/dist/checks/missing-princ.js +1 -1
  23. package/dist/checks/multiple-setq.js +1 -1
  24. package/dist/checks/no-return.d.ts +2 -1
  25. package/dist/checks/no-return.js +4 -3
  26. package/dist/checks/nth-usage.js +1 -1
  27. package/dist/checks/parameter-naming.js +2 -2
  28. package/dist/checks/quote-vs-function.js +1 -1
  29. package/dist/checks/recursive-call.js +1 -1
  30. package/dist/checks/redundant-let.js +1 -1
  31. package/dist/checks/redundant-nil-else.js +1 -1
  32. package/dist/checks/redundant-progn.js +1 -1
  33. package/dist/checks/redundant-setq.js +1 -1
  34. package/dist/checks/self-compare.js +1 -1
  35. package/dist/checks/setq-single-arg.js +3 -2
  36. package/dist/checks/single-arg-and-or.js +1 -1
  37. package/dist/checks/undeclared-setq.d.ts +2 -1
  38. package/dist/checks/undeclared-setq.js +6 -5
  39. package/dist/checks/unused-let.js +3 -3
  40. package/dist/checks/unused-local-fun.js +2 -2
  41. package/dist/checks/unused-package-dep.d.ts +0 -1
  42. package/dist/checks/unused-package-dep.js +6 -93
  43. package/dist/checks/unused-param.js +2 -2
  44. package/dist/checks/unused-variable.js +2 -2
  45. package/dist/checks/variable-shadow.js +3 -3
  46. package/dist/checks/while-constant.js +1 -1
  47. package/dist/cli/collector.js +26 -19
  48. package/dist/config.d.ts +1 -1
  49. package/dist/config.js +18 -9
  50. package/dist/fixes/index.d.ts +4 -0
  51. package/dist/fixes/index.js +843 -140
  52. package/dist/formatter.d.ts +0 -1
  53. package/dist/formatter.js +141 -31
  54. package/dist/index.js +59 -51
  55. package/dist/project.d.ts +1 -1
  56. package/dist/project.js +98 -112
  57. package/dist/runner.d.ts +1 -1
  58. package/dist/runner.js +17 -8
  59. package/dist/utils.js +21 -30
  60. package/dist/validate.js +3 -3
  61. package/dist/visitor-runner.d.ts +0 -28
  62. package/dist/visitor-runner.js +3 -809
  63. package/dist/visitors/equal-nil.d.ts +2 -2
  64. package/dist/visitors/equal-nil.js +5 -4
  65. package/dist/visitors/index.d.ts +1 -1
  66. package/dist/visitors/progn-in-cond.d.ts +2 -2
  67. package/dist/visitors/progn-in-cond.js +5 -4
  68. package/dist/visitors/redundant-and-or.d.ts +2 -2
  69. package/dist/visitors/redundant-and-or.js +3 -3
  70. package/dist/visitors/redundant-car-cdr.d.ts +2 -2
  71. package/dist/visitors/redundant-car-cdr.js +3 -3
  72. package/dist/visitors/register.d.ts +6 -0
  73. package/dist/visitors/register.js +625 -0
  74. package/dist/visitors/shared.d.ts +40 -0
  75. package/dist/visitors/shared.js +129 -0
  76. package/dist/visitors/types.d.ts +2 -7
  77. package/dist/visitors/types.js +0 -69
  78. package/dist/watch.js +33 -12
  79. package/dist/worker.js +1 -1
  80. package/package.json +2 -2
@@ -12,7 +12,7 @@ function checkRedundantLetAst(ast, file) {
12
12
  const issues = [];
13
13
  const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
14
14
  for (const node of letNodes) {
15
- if (!node.children || node.children.length < 2)
15
+ if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
16
16
  continue;
17
17
  const bindings = node.children[1];
18
18
  // (let () ...) — binding list exists but is empty
@@ -13,7 +13,7 @@ function checkRedundantNilElseAst(ast, file) {
13
13
  // Find (if cond then nil) — else branch is nil which is the default
14
14
  const ifNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'if'));
15
15
  for (const node of ifNodes) {
16
- if (!node.children || node.children.length !== 4)
16
+ if (!(0, parser_1.astIsList)(node) || node.children.length !== 4)
17
17
  continue;
18
18
  // (if cond then nil) — 4 children: if, cond, then, nil-else
19
19
  const elseBranch = node.children[3];
@@ -12,7 +12,7 @@ function checkRedundantPrognAst(ast, file) {
12
12
  const issues = [];
13
13
  const prognNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'progn'));
14
14
  for (const node of prognNodes) {
15
- if (!node.children)
15
+ if (!(0, parser_1.astIsList)(node))
16
16
  continue;
17
17
  // count forms after 'progn'
18
18
  const bodyCount = node.children.length - 1;
@@ -12,7 +12,7 @@ function checkRedundantSetqAst(ast, file) {
12
12
  const issues = [];
13
13
  const setqNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
14
14
  for (const node of setqNodes) {
15
- if (!node.children)
15
+ if (!(0, parser_1.astIsList)(node))
16
16
  continue;
17
17
  // (setq x x) — variable is set to itself
18
18
  for (let i = 1; i < node.children.length - 1; i += 2) {
@@ -14,7 +14,7 @@ function checkSelfCompareAst(ast, file) {
14
14
  for (const fn of COMPARE_FUNCTIONS) {
15
15
  const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, fn));
16
16
  for (const node of nodes) {
17
- if (!node.children)
17
+ if (!(0, parser_1.astIsList)(node))
18
18
  continue;
19
19
  // (= x x) — 3 children: '=', x, x
20
20
  if (node.children.length === 3) {
@@ -12,16 +12,17 @@ function checkSetqSingleArgAst(ast, file) {
12
12
  const issues = [];
13
13
  const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'setq'));
14
14
  for (const node of nodes) {
15
- if (!node.children)
15
+ if (!(0, parser_1.astIsList)(node))
16
16
  continue;
17
17
  // (setq x) — 2 children: 'setq', x — missing value
18
18
  if (node.children.length === 2) {
19
+ const varNode = node.children[1];
19
20
  issues.push({
20
21
  file,
21
22
  line: node.pos.line,
22
23
  severity: 'warn',
23
24
  rule: 'setq_single_arg',
24
- message: (0, locale_1.t)('setq_single_arg', node.children[1].name || ''),
25
+ message: (0, locale_1.t)('setq_single_arg', varNode.type === 'symbol' && varNode.name ? varNode.name : ''),
25
26
  });
26
27
  }
27
28
  }
@@ -13,7 +13,7 @@ function checkSingleArgAndOrAst(ast, file) {
13
13
  for (const form of ['and', 'or']) {
14
14
  const nodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, form));
15
15
  for (const node of nodes) {
16
- if (!node.children)
16
+ if (!(0, parser_1.astIsList)(node))
17
17
  continue;
18
18
  // (and x) — 2 children: 'and', x
19
19
  if (node.children.length === 2) {
@@ -1,3 +1,4 @@
1
1
  import { Issue } from '../types';
2
- export declare function checkUndeclaredSetq(content: string, file: string): Issue[];
2
+ import { AstNode } from '@atlisp/parser';
3
+ export declare function checkUndeclaredSetq(content: string, file: string, ast?: AstNode): Issue[];
3
4
  //# sourceMappingURL=undeclared-setq.d.ts.map
@@ -6,10 +6,10 @@ const parser_1 = require("@atlisp/parser");
6
6
  /** Extract declared variable names from a defun's parameter list (both sides of /) */
7
7
  function getDeclaredVars(defun) {
8
8
  const vars = new Set();
9
- if (!defun.children || defun.children.length < 3)
9
+ if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
10
10
  return vars;
11
11
  const paramList = defun.children[2];
12
- if (paramList.type !== 'list' || !paramList.children)
12
+ if (!(0, parser_1.astIsList)(paramList))
13
13
  return vars;
14
14
  for (const p of paramList.children) {
15
15
  if (p.type === 'symbol' && p.name && p.name !== '/' && p.name !== 'T' && p.name !== 'nil') {
@@ -40,12 +40,13 @@ function findEnclosingDefun(node) {
40
40
  }
41
41
  return null;
42
42
  }
43
- function checkUndeclaredSetq(content, file) {
43
+ function checkUndeclaredSetq(content, file, ast) {
44
44
  const issues = [];
45
- const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
45
+ if (!ast)
46
+ ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
46
47
  const setqForms = (0, parser_1.astFindListWithHead)(ast, 'setq');
47
48
  for (const setq of setqForms) {
48
- if (!setq.children || setq.children.length < 2)
49
+ if (!(0, parser_1.astIsList)(setq) || setq.children.length < 2)
49
50
  continue;
50
51
  // Skip top-level setq (not inside any defun)
51
52
  const defun = findEnclosingDefun(setq);
@@ -12,15 +12,15 @@ function checkUnusedLetBindingAst(ast, file) {
12
12
  const issues = [];
13
13
  const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
14
14
  for (const node of letNodes) {
15
- if (!node.children || node.children.length < 2)
15
+ if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
16
16
  continue;
17
17
  const bindingList = node.children[1];
18
- if (bindingList.type !== 'list' || !bindingList.children)
18
+ if (!(0, parser_1.astIsList)(bindingList))
19
19
  continue;
20
20
  // Collect let-bound variables
21
21
  const bindings = [];
22
22
  for (const binding of bindingList.children) {
23
- if (binding.type !== 'list' || !binding.children || binding.children.length === 0)
23
+ if (!(0, parser_1.astIsList)(binding) || binding.children.length === 0)
24
24
  continue;
25
25
  const varNode = binding.children[0];
26
26
  if (varNode.type !== 'symbol' || !varNode.name)
@@ -12,10 +12,10 @@ function checkUnusedLocalFunAst(ast, file) {
12
12
  const issues = [];
13
13
  const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
14
14
  for (const defun of defuns) {
15
- if (!defun.children || defun.children.length < 3)
15
+ if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
16
16
  continue;
17
17
  const paramList = defun.children[2];
18
- if (paramList.type !== 'list' || !paramList.children)
18
+ if (!(0, parser_1.astIsList)(paramList))
19
19
  continue;
20
20
  // Collect local variables after `/` in param list
21
21
  // (defun foo (x / y z) ...) → y, z are local vars
@@ -1,4 +1,3 @@
1
1
  import { Issue } from '../types';
2
- export declare function checkUnusedPackageDep(file: string): Issue[];
3
2
  export declare function checkUnusedPackageDepFromContent(file: string, content: string): Issue[];
4
3
  //# sourceMappingURL=unused-package-dep.d.ts.map
@@ -1,96 +1,8 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.checkUnusedPackageDep = checkUnusedPackageDep;
37
3
  exports.checkUnusedPackageDepFromContent = checkUnusedPackageDepFromContent;
38
4
  const locale_1 = require("../locale");
39
5
  const parser_1 = require("@atlisp/parser");
40
- const fs = __importStar(require("fs"));
41
- function checkUnusedPackageDep(file) {
42
- const issues = [];
43
- const content = fs.readFileSync(file, 'utf-8');
44
- const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
45
- const inPackageNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'in-package'));
46
- if (inPackageNodes.length === 0)
47
- return issues;
48
- const allPackages = new Set();
49
- for (const node of inPackageNodes) {
50
- if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
51
- allPackages.add(node.children[1].name);
52
- }
53
- }
54
- const usedSymbols = new Set();
55
- const allLists = (0, parser_1.astFindAll)(ast, n => n.type === 'list');
56
- for (const node of allLists) {
57
- if (!node.children || node.children.length === 0)
58
- continue;
59
- const car = node.children[0];
60
- if (car.type === 'symbol' && car.name) {
61
- const parts = car.name.split(':');
62
- if (parts.length === 2 && parts[0] && parts[1]) {
63
- usedSymbols.add(parts[0]);
64
- }
65
- }
66
- for (let i = 1; i < node.children.length; i++) {
67
- const child = node.children[i];
68
- if (child.type === 'symbol' && child.name) {
69
- const parts = child.name.split(':');
70
- if (parts.length === 2 && parts[0] && parts[1]) {
71
- usedSymbols.add(parts[0]);
72
- }
73
- }
74
- }
75
- }
76
- const mainPackage = inPackageNodes.length > 0 && inPackageNodes[0].children && inPackageNodes[0].children[1]
77
- ? inPackageNodes[0].children[1].name || ''
78
- : '';
79
- allPackages.delete(mainPackage);
80
- for (const pkg of allPackages) {
81
- if (!usedSymbols.has(pkg)) {
82
- const node = inPackageNodes.find(n => n.children && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === pkg);
83
- issues.push({
84
- file,
85
- line: node ? node.pos.line : 1,
86
- severity: 'warn',
87
- rule: 'unused_package_dep',
88
- message: (0, locale_1.t)('unused_package_dep', pkg),
89
- });
90
- }
91
- }
92
- return issues;
93
- }
94
6
  function checkUnusedPackageDepFromContent(file, content) {
95
7
  const issues = [];
96
8
  const ast = (0, parser_1.parseAst)(content, { errorRecovery: true });
@@ -99,14 +11,14 @@ function checkUnusedPackageDepFromContent(file, content) {
99
11
  return issues;
100
12
  const allPackages = new Set();
101
13
  for (const node of inPackageNodes) {
102
- if (node.children && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
14
+ if ((0, parser_1.astIsList)(node) && node.children.length >= 2 && (0, parser_1.astIsSymbol)(node.children[1])) {
103
15
  allPackages.add(node.children[1].name);
104
16
  }
105
17
  }
106
18
  const usedSymbols = new Set();
107
19
  const allLists = (0, parser_1.astFindAll)(ast, n => n.type === 'list');
108
20
  for (const node of allLists) {
109
- if (!node.children || node.children.length === 0)
21
+ if (!(0, parser_1.astIsList)(node) || node.children.length === 0)
110
22
  continue;
111
23
  const car = node.children[0];
112
24
  if (car.type === 'symbol' && car.name) {
@@ -125,13 +37,14 @@ function checkUnusedPackageDepFromContent(file, content) {
125
37
  }
126
38
  }
127
39
  }
128
- const mainPackage = inPackageNodes.length > 0 && inPackageNodes[0].children && inPackageNodes[0].children[1]
129
- ? inPackageNodes[0].children[1].name || ''
40
+ const mainPkgNode = inPackageNodes.length > 0 && (0, parser_1.astIsList)(inPackageNodes[0]) ? inPackageNodes[0] : null;
41
+ const mainPackage = mainPkgNode && mainPkgNode.children.length >= 2 && (0, parser_1.astIsSymbol)(mainPkgNode.children[1])
42
+ ? mainPkgNode.children[1].name || ''
130
43
  : '';
131
44
  allPackages.delete(mainPackage);
132
45
  for (const pkg of allPackages) {
133
46
  if (!usedSymbols.has(pkg)) {
134
- const node = inPackageNodes.find(n => n.children && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === pkg);
47
+ const node = inPackageNodes.find(n => (0, parser_1.astIsList)(n) && n.children.length >= 2 && (0, parser_1.astIsSymbol)(n.children[1]) && n.children[1].name === pkg);
135
48
  issues.push({
136
49
  file,
137
50
  line: node ? node.pos.line : 1,
@@ -12,14 +12,14 @@ function checkUnusedParameterAst(ast, file) {
12
12
  const issues = [];
13
13
  const defuns = (0, parser_1.astFindListWithHead)(ast, 'defun');
14
14
  for (const defun of defuns) {
15
- if (!defun.children || defun.children.length < 3)
15
+ if (!(0, parser_1.astIsList)(defun) || defun.children.length < 3)
16
16
  continue;
17
17
  const nameNode = defun.children[1];
18
18
  const funcName = nameNode.type === 'symbol' && nameNode.name ? nameNode.name : '<unknown>';
19
19
  if (funcName.startsWith('c:') || funcName.startsWith('C:'))
20
20
  continue;
21
21
  const paramList = defun.children[2];
22
- if (paramList.type !== 'list' || !paramList.children)
22
+ if (!(0, parser_1.astIsList)(paramList))
23
23
  continue;
24
24
  // Collect params before / (AutoLISP: (defun foo (a b / x y) ...) — a,b are params, x,y are locals)
25
25
  const params = [];
@@ -14,7 +14,7 @@ function checkUnusedVariableAst(ast, file) {
14
14
  const setqForms = (0, parser_1.astFindListWithHead)(ast, 'setq');
15
15
  const setqVars = new Map();
16
16
  for (const form of setqForms) {
17
- if (!form.children)
17
+ if (!(0, parser_1.astIsList)(form))
18
18
  continue;
19
19
  for (let i = 1; i < form.children.length; i += 2) {
20
20
  const v = form.children[i];
@@ -37,7 +37,7 @@ function checkUnusedVariableAst(ast, file) {
37
37
  // Set of defined var nodes (to exclude from usage)
38
38
  const defNodes = new Set();
39
39
  for (const form of setqForms) {
40
- if (!form.children)
40
+ if (!(0, parser_1.astIsList)(form))
41
41
  continue;
42
42
  for (let i = 1; i < form.children.length; i += 2) {
43
43
  defNodes.add(form.children[i]);
@@ -12,15 +12,15 @@ function checkVariableShadowAst(ast, file) {
12
12
  const issues = [];
13
13
  const letNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'let'));
14
14
  for (const node of letNodes) {
15
- if (!node.children || node.children.length < 2)
15
+ if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
16
16
  continue;
17
17
  const bindingList = node.children[1];
18
- if (bindingList.type !== 'list' || !bindingList.children)
18
+ if (!(0, parser_1.astIsList)(bindingList))
19
19
  continue;
20
20
  // Collect let-bound variable names
21
21
  const letVars = new Set();
22
22
  for (const binding of bindingList.children) {
23
- if (binding.type !== 'list' || !binding.children || binding.children.length === 0)
23
+ if (!(0, parser_1.astIsList)(binding) || binding.children.length === 0)
24
24
  continue;
25
25
  const varNode = binding.children[0];
26
26
  if (varNode.type !== 'symbol' || !varNode.name)
@@ -12,7 +12,7 @@ function checkWhileConstantAst(ast, file) {
12
12
  const issues = [];
13
13
  const whileNodes = (0, parser_1.astFindAll)(ast, n => (0, parser_1.astIsList)(n, 'while'));
14
14
  for (const node of whileNodes) {
15
- if (!node.children || node.children.length < 2)
15
+ if (!(0, parser_1.astIsList)(node) || node.children.length < 2)
16
16
  continue;
17
17
  const cond = node.children[1];
18
18
  // Check for constant conditions: nil, T, 1, 0, etc.
@@ -41,6 +41,22 @@ const path = __importStar(require("path"));
41
41
  const child_process_1 = require("child_process");
42
42
  const GLOB_PLACEHOLDER = '\x00GLOB\x00';
43
43
  const GLOB_RE = new RegExp(GLOB_PLACEHOLDER, 'g');
44
+ const globCache = new Map();
45
+ function toRegex(pattern) {
46
+ const cached = globCache.get(pattern);
47
+ if (cached)
48
+ return cached;
49
+ const str = pattern
50
+ .replace(/\./g, '\\.')
51
+ .replace(/\*\*\//g, GLOB_PLACEHOLDER)
52
+ .replace(/\*\*/g, '.*')
53
+ .replace(/\*/g, '[^/]*')
54
+ .replace(/\?/g, '.')
55
+ .replace(GLOB_RE, '(.*/)?');
56
+ const re = new RegExp(`^${str}$`);
57
+ globCache.set(pattern, re);
58
+ return re;
59
+ }
44
60
  function* walkFiles(dir, rootDir, regex) {
45
61
  let entries;
46
62
  try {
@@ -88,15 +104,7 @@ function collectChangedFiles(rootDir, baseBranch) {
88
104
  }
89
105
  }
90
106
  function globFiles(pattern, rootDir) {
91
- const regexStr = pattern
92
- .replace(/\./g, '\\.')
93
- .replace(/\*\*\//g, GLOB_PLACEHOLDER)
94
- .replace(/\*\*/g, '.*')
95
- .replace(/\*/g, '[^/]*')
96
- .replace(/\?/g, '.')
97
- .replace(GLOB_RE, '(.*/)?');
98
- const regex = new RegExp(`^${regexStr}$`);
99
- return Array.from(walkFiles(rootDir, rootDir, regex));
107
+ return Array.from(walkFiles(rootDir, rootDir, toRegex(pattern)));
100
108
  }
101
109
  function collectFiles(rootDir, opts, config) {
102
110
  const files = new Set();
@@ -127,23 +135,22 @@ function collectFiles(rootDir, opts, config) {
127
135
  }
128
136
  }
129
137
  }
130
- const excludePatterns = config.source.exclude.map(e => {
131
- const str = e
138
+ const excludeRe = config.source.exclude.length > 0
139
+ ? new RegExp(`^(${config.source.exclude
140
+ .map(e => e
132
141
  .replace(/\./g, '\\.')
133
142
  .replace(/\*\*\//g, GLOB_PLACEHOLDER)
134
143
  .replace(/\*\*/g, '.*')
135
144
  .replace(/\*/g, '[^/]*')
136
- .replace(GLOB_RE, '(.*/)?');
137
- return new RegExp(`^${str}$`);
138
- });
145
+ .replace(GLOB_RE, '(.*/)?'))
146
+ .join('|')})$`)
147
+ : null;
139
148
  return Array.from(files)
140
149
  .filter(f => {
150
+ if (!excludeRe)
151
+ return true;
141
152
  const rel = path.relative(rootDir, f).replace(/\\/g, '/');
142
- for (const re of excludePatterns) {
143
- if (re.test(rel))
144
- return false;
145
- }
146
- return true;
153
+ return !excludeRe.test(rel);
147
154
  })
148
155
  .sort();
149
156
  }
package/dist/config.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LintConfig } from './types';
2
2
  export declare function loadConfig(configPath?: string): LintConfig;
3
- export declare function getCheckSeverity(config: LintConfig, rule: string): string;
4
3
  export declare function findOverrides(filepath: string): Partial<LintConfig> | null;
4
+ export declare function clearOverrideCache(): void;
5
5
  export declare function mergeOverrides(base: LintConfig, overrides: Partial<LintConfig>): LintConfig;
6
6
  //# sourceMappingURL=config.d.ts.map
package/dist/config.js CHANGED
@@ -34,12 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.loadConfig = loadConfig;
37
- exports.getCheckSeverity = getCheckSeverity;
38
37
  exports.findOverrides = findOverrides;
38
+ exports.clearOverrideCache = clearOverrideCache;
39
39
  exports.mergeOverrides = mergeOverrides;
40
40
  const fs = __importStar(require("fs"));
41
41
  const path = __importStar(require("path"));
42
42
  const presets_1 = require("./presets");
43
+ const overrideCache = new Map();
43
44
  const DEFAULT_CONFIG = {
44
45
  locale: 'zh',
45
46
  source: {
@@ -199,7 +200,7 @@ function loadConfig(configPath) {
199
200
  const raw = fs.readFileSync(absPath, 'utf-8');
200
201
  const user = JSON.parse(raw);
201
202
  // Start from default config
202
- const merged = JSON.parse(JSON.stringify(DEFAULT_CONFIG));
203
+ const merged = structuredClone(DEFAULT_CONFIG);
203
204
  // Apply preset if specified (preset applies before user overrides)
204
205
  const preset = user.preset;
205
206
  if (preset && presets_1.PRESETS[preset]) {
@@ -211,7 +212,7 @@ function loadConfig(configPath) {
211
212
  delete merged.preset;
212
213
  return merged;
213
214
  }
214
- return JSON.parse(JSON.stringify(DEFAULT_CONFIG));
215
+ return structuredClone(DEFAULT_CONFIG);
215
216
  }
216
217
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
217
218
  function deepMerge(target, source) {
@@ -228,29 +229,37 @@ function deepMerge(target, source) {
228
229
  }
229
230
  }
230
231
  }
231
- function getCheckSeverity(config, rule) {
232
- return config.checks[rule] || 'off';
233
- }
234
232
  function findOverrides(filepath) {
233
+ const cached = overrideCache.get(filepath);
234
+ if (cached !== undefined)
235
+ return cached;
235
236
  let dir = path.dirname(filepath);
236
237
  for (;;) {
237
238
  const configPath = path.join(dir, '.atlisp-lint.json');
238
239
  if (fs.existsSync(configPath)) {
239
240
  try {
240
- return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
241
+ const result = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
242
+ overrideCache.set(filepath, result);
243
+ return result;
241
244
  }
242
245
  catch {
246
+ overrideCache.set(filepath, null);
243
247
  return null;
244
248
  }
245
249
  }
246
250
  const parent = path.dirname(dir);
247
- if (parent === dir)
251
+ if (parent === dir) {
252
+ overrideCache.set(filepath, null);
248
253
  return null;
254
+ }
249
255
  dir = parent;
250
256
  }
251
257
  }
258
+ function clearOverrideCache() {
259
+ overrideCache.clear();
260
+ }
252
261
  function mergeOverrides(base, overrides) {
253
- const merged = JSON.parse(JSON.stringify(base));
262
+ const merged = structuredClone(base);
254
263
  deepMerge(merged, overrides);
255
264
  return merged;
256
265
  }
@@ -1,8 +1,12 @@
1
1
  import { Issue } from '../types';
2
+ import { AstNode, AstPosition } from '@atlisp/parser';
2
3
  export type FixResult = string | null;
3
4
  export interface FixProvider {
4
5
  (line: string): string;
5
6
  }
7
+ export interface ContentFixProvider {
8
+ (issues: Issue[], content: string, ast?: AstNode, posToOffset?: (pos: AstPosition) => number): string;
9
+ }
6
10
  export declare const FIXABLE_RULES: Set<string>;
7
11
  export declare function applyFixesFromProviders(issues: Issue[], content: string, filepath: string): string;
8
12
  //# sourceMappingURL=index.d.ts.map