@sasjs/lint 2.7.0 → 3.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/README.md CHANGED
@@ -40,7 +40,9 @@ Configuration is via a `.sasjslint` file with the following structure (these are
40
40
  "noSpacesInFileNames": true,
41
41
  "noTabs": true,
42
42
  "noTrailingSpaces": true,
43
- "defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/"
43
+ "noUndeclaredMacros": true,
44
+ "noUnusedMacros": true,
45
+ "defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/"
44
46
  }
45
47
  ```
46
48
 
@@ -79,10 +81,14 @@ The default header is as follows:
79
81
  /**
80
82
  @file
81
83
  @brief <Your brief here>
84
+
82
85
  <h4> SAS Macros </h4>
86
+
83
87
  **/
84
88
  ```
85
89
 
90
+ A blank line separates the `<h4> SAS Macros </h4>` section from the header content before it and from whatever follows the list. The same blank-line convention applies to the `<h4> Other Macros </h4>` section.
91
+
86
92
  If creating a new value, use `{lineEnding}` instead of `\n`, eg as follows:
87
93
 
88
94
  ```json
@@ -289,6 +295,27 @@ This will highlight lines with trailing spaces. Trailing spaces serve no useful
289
295
  - Default: true
290
296
  - severity: WARNING
291
297
 
298
+ ### noUndeclaredMacros
299
+
300
+ SASjs programs declare the macros they use in the `<h4> SAS Macros </h4>` section of the header, so that the compiler can assemble the job before it runs. This rule reports a warning for each macro that a file invokes without declaring it - either by defining it in the file, or by listing it under `<h4> SAS Macros </h4>` or `<h4> Other Macros </h4>`.
301
+
302
+ The macros that ship with SAS (`%scan`, `%index`, `%sysfunc` and the other macro functions, along with the macro language keywords such as `%if`, `%then` and `%do`) are always considered declared, so they are never reported. That list is generated from the `macroStatements` and `macroFunctions` groups of `@sasjs/sas-language` (`npm run generate:macros`), so it tracks the SAS language data rather than being maintained here. Macros made available through the `SASAUTOS` system option are not visible to the linter and will be reported.
303
+
304
+ The warning is resolved by adding the macro name to the header. Running `sasjs lint fix` (or saving in the SASjs VS Code extension with `formatOnSave`) adds the missing macros automatically, as a unique list sorted alphabetically.
305
+
306
+ - Default: true
307
+ - Severity: WARNING
308
+
309
+ ### noUnusedMacros
310
+
311
+ The counterpart of `noUndeclaredMacros`. This rule reports a warning for each macro listed under `<h4> SAS Macros </h4>` that the file itself never invokes, which usually means the header is out of date.
312
+
313
+ A macro can be referenced indirectly - for example by another macro in the dependency chain, or through a dynamic `%&macro` call - without appearing as a direct invocation in the file, so the warning is advisory.
314
+
315
+ Running `sasjs lint fix` (or saving in the SASjs VS Code extension with `formatOnSave`) removes the unused entries from the header. Together with `noUndeclaredMacros`, this leaves the `<h4> SAS Macros </h4>` section listing exactly the macros the file uses.
316
+
317
+ - Default: true
318
+ - Severity: WARNING
292
319
 
293
320
  ## severityLevel
294
321
 
@@ -318,6 +345,8 @@ We've already implemented the following rules:
318
345
 
319
346
  - Add the macro name to the %mend statement
320
347
  - Add a doxygen header template if none exists
348
+ - Add undeclared macros to the `<h4> SAS Macros </h4>` section
349
+ - Remove unused macros from the `<h4> SAS Macros </h4>` section
321
350
  - Remove trailing spaces
322
351
 
323
352
  We're looking to implement the following rules:
package/package.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "scripts": {
5
5
  "test": "jest --coverage",
6
6
  "build": "rimraf build && tsc",
7
+ "generate:macros": "node ./scripts/generate-macro-names.mjs",
7
8
  "preinstall": "node checkNodeVersion",
8
9
  "prebuild": "node checkNodeVersion",
9
10
  "publishInit": "cp -r ./build/* . && rm -rf ./build && rm -rf ./src && rm tsconfig.json",
@@ -59,6 +60,7 @@
59
60
  "homepage": "https://github.com/sasjs/lint#readme",
60
61
  "devDependencies": {
61
62
  "@nogoo9/gitleaks": "8.30.1-post.2",
63
+ "@sasjs/sas-language": "0.3.0",
62
64
  "@types/jest": "30.0.0",
63
65
  "@types/node": "26.6.3",
64
66
  "jest": "30.5.2",
@@ -70,5 +72,5 @@
70
72
  "@sasjs/utils": "3.6.2",
71
73
  "ignore": "7.0.11"
72
74
  },
73
- "version": "2.7.0"
75
+ "version": "3.0.0"
74
76
  }
@@ -3,6 +3,8 @@ export { hasMacroNameInMend } from './hasMacroNameInMend';
3
3
  export { hasMacroParentheses } from './hasMacroParentheses';
4
4
  export { lineEndings } from './lineEndings';
5
5
  export { noNestedMacros } from './noNestedMacros';
6
+ export { noUndeclaredMacros } from './noUndeclaredMacros';
7
+ export { noUnusedMacros } from './noUnusedMacros';
6
8
  export { strictMacroDefinition } from './strictMacroDefinition';
7
9
  export { hasRequiredMacroOptions } from './hasRequiredMacroOptions';
8
10
  export { noSingleAsteriskComments } from './noSingleAsteriskComments';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noNestedMacros = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
3
+ exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noNestedMacros = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
4
4
  var hasDoxygenHeader_1 = require("./hasDoxygenHeader");
5
5
  Object.defineProperty(exports, "hasDoxygenHeader", { enumerable: true, get: function () { return hasDoxygenHeader_1.hasDoxygenHeader; } });
6
6
  var hasMacroNameInMend_1 = require("./hasMacroNameInMend");
@@ -11,6 +11,10 @@ var lineEndings_1 = require("./lineEndings");
11
11
  Object.defineProperty(exports, "lineEndings", { enumerable: true, get: function () { return lineEndings_1.lineEndings; } });
12
12
  var noNestedMacros_1 = require("./noNestedMacros");
13
13
  Object.defineProperty(exports, "noNestedMacros", { enumerable: true, get: function () { return noNestedMacros_1.noNestedMacros; } });
14
+ var noUndeclaredMacros_1 = require("./noUndeclaredMacros");
15
+ Object.defineProperty(exports, "noUndeclaredMacros", { enumerable: true, get: function () { return noUndeclaredMacros_1.noUndeclaredMacros; } });
16
+ var noUnusedMacros_1 = require("./noUnusedMacros");
17
+ Object.defineProperty(exports, "noUnusedMacros", { enumerable: true, get: function () { return noUnusedMacros_1.noUnusedMacros; } });
14
18
  var strictMacroDefinition_1 = require("./strictMacroDefinition");
15
19
  Object.defineProperty(exports, "strictMacroDefinition", { enumerable: true, get: function () { return strictMacroDefinition_1.strictMacroDefinition; } });
16
20
  var hasRequiredMacroOptions_1 = require("./hasRequiredMacroOptions");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/file/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,6CAA2C;AAAlC,0GAAA,WAAW,OAAA;AACpB,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,qEAAmE;AAA1D,kIAAA,uBAAuB,OAAA;AAChC,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/rules/file/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,6DAA2D;AAAlD,0HAAA,mBAAmB,OAAA;AAC5B,6CAA2C;AAAlC,0GAAA,WAAW,OAAA;AACpB,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,2DAAyD;AAAhD,wHAAA,kBAAkB,OAAA;AAC3B,mDAAiD;AAAxC,gHAAA,cAAc,OAAA;AACvB,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,qEAAmE;AAA1D,kIAAA,uBAAuB,OAAA;AAChC,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA"}
@@ -0,0 +1,7 @@
1
+ import { FileLintRule } from '../../types/LintRule';
2
+ /**
3
+ * Lint rule that reports each macro a file uses without declaring it in the
4
+ * header. The formatter resolves the warning by adding the macro names to the
5
+ * `<h4> SAS Macros </h4>` section.
6
+ */
7
+ export declare const noUndeclaredMacros: FileLintRule;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUndeclaredMacros = void 0;
4
+ const types_1 = require("../../types");
5
+ const LintRuleType_1 = require("../../types/LintRuleType");
6
+ const Severity_1 = require("../../types/Severity");
7
+ const headerMacros_1 = require("../../utils/headerMacros");
8
+ const parseMacroCalls_1 = require("../../utils/parseMacroCalls");
9
+ const parseMacros_1 = require("../../utils/parseMacros");
10
+ const sasMacros_1 = require("../../utils/sasMacros");
11
+ const name = 'noUndeclaredMacros';
12
+ const description = 'Enforce that every macro used in a file is declared.';
13
+ const message = `Macro '{macro}' is not declared - add it to the <h4> SAS Macros </h4> or <h4> Other Macros </h4> section of the header`;
14
+ /**
15
+ * Collects the first invocation of each macro that the file uses but does not
16
+ * declare. A macro is declared when it is defined in the file, or listed in the
17
+ * `<h4> SAS Macros </h4>` or `<h4> Other Macros </h4>` section of its header.
18
+ * Macros that ship with SAS are always considered declared.
19
+ */
20
+ const getUndeclaredCalls = (value, config) => {
21
+ if (!value)
22
+ return [];
23
+ const declared = new Set();
24
+ (0, parseMacros_1.parseMacros)(value, config).forEach((macro) => {
25
+ if (macro.name)
26
+ declared.add(macro.name.toLowerCase());
27
+ });
28
+ (0, headerMacros_1.getHeaderMacros)(value, config).forEach((macro) => {
29
+ declared.add(macro.name.toLowerCase());
30
+ });
31
+ const undeclared = [];
32
+ const seen = new Set();
33
+ (0, parseMacroCalls_1.parseMacroCalls)(value, config).forEach((call) => {
34
+ const key = call.name.toLowerCase();
35
+ if (seen.has(key))
36
+ return;
37
+ seen.add(key);
38
+ if ((0, sasMacros_1.isSasMacro)(call.name))
39
+ return;
40
+ if (declared.has(key))
41
+ return;
42
+ undeclared.push(call);
43
+ });
44
+ return undeclared;
45
+ };
46
+ const test = (value, config) => {
47
+ const cfg = config || new types_1.LintConfig();
48
+ const severity = cfg.severityLevel[name] || Severity_1.Severity.Warning;
49
+ return getUndeclaredCalls(value, cfg).map((call) => ({
50
+ message: message.replace('{macro}', call.name),
51
+ lineNumber: call.lineNumber,
52
+ startColumnNumber: call.startColumnNumber,
53
+ endColumnNumber: call.endColumnNumber,
54
+ severity
55
+ }));
56
+ };
57
+ const fix = (value, config) => {
58
+ const cfg = config || new types_1.LintConfig();
59
+ const macros = getUndeclaredCalls(value, cfg).map((call) => call.name);
60
+ if (!macros.length)
61
+ return value;
62
+ return (0, headerMacros_1.addMacrosToHeader)(value, cfg, macros);
63
+ };
64
+ /**
65
+ * Lint rule that reports each macro a file uses without declaring it in the
66
+ * header. The formatter resolves the warning by adding the macro names to the
67
+ * `<h4> SAS Macros </h4>` section.
68
+ */
69
+ exports.noUndeclaredMacros = {
70
+ type: LintRuleType_1.LintRuleType.File,
71
+ name,
72
+ description,
73
+ message,
74
+ test,
75
+ fix
76
+ };
77
+ //# sourceMappingURL=noUndeclaredMacros.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noUndeclaredMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUndeclaredMacros.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { FileLintRule } from '../../types/LintRule';
2
+ /**
3
+ * Lint rule that reports macros declared in the `<h4> SAS Macros </h4>` section
4
+ * of a file header that the file itself never invokes. The formatter removes
5
+ * them from the header.
6
+ */
7
+ export declare const noUnusedMacros: FileLintRule;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noUnusedMacros = void 0;
4
+ const types_1 = require("../../types");
5
+ const LintRuleType_1 = require("../../types/LintRuleType");
6
+ const Severity_1 = require("../../types/Severity");
7
+ const headerMacros_1 = require("../../utils/headerMacros");
8
+ const parseMacroCalls_1 = require("../../utils/parseMacroCalls");
9
+ const name = 'noUnusedMacros';
10
+ const description = 'Report macros declared in the <h4> SAS Macros </h4> section that the file does not use.';
11
+ const message = `Macro '{macro}' is declared in the <h4> SAS Macros </h4> section but not used in the file`;
12
+ /**
13
+ * Collects the macros declared in the `<h4> SAS Macros </h4>` section that the
14
+ * file itself never invokes. A macro that appears more than once is reported
15
+ * once, at its first entry.
16
+ */
17
+ const getUnusedMacros = (value, config) => {
18
+ if (!value)
19
+ return [];
20
+ const used = new Set((0, parseMacroCalls_1.parseMacroCalls)(value, config).map((call) => call.name.toLowerCase()));
21
+ const unused = [];
22
+ const reported = new Set();
23
+ (0, headerMacros_1.getHeaderMacros)(value, config)
24
+ .filter((macro) => macro.section === 'sasMacros')
25
+ .forEach((macro) => {
26
+ const key = macro.name.toLowerCase();
27
+ if (reported.has(key))
28
+ return;
29
+ reported.add(key);
30
+ if (used.has(key))
31
+ return;
32
+ unused.push(macro);
33
+ });
34
+ return unused;
35
+ };
36
+ const test = (value, config) => {
37
+ const cfg = config || new types_1.LintConfig();
38
+ const severity = cfg.severityLevel[name] || Severity_1.Severity.Warning;
39
+ return getUnusedMacros(value, cfg).map((macro) => ({
40
+ message: message.replace('{macro}', macro.name),
41
+ lineNumber: macro.lineNumber,
42
+ startColumnNumber: macro.startColumnNumber,
43
+ endColumnNumber: macro.endColumnNumber,
44
+ severity
45
+ }));
46
+ };
47
+ const fix = (value, config) => {
48
+ const cfg = config || new types_1.LintConfig();
49
+ const macros = getUnusedMacros(value, cfg).map((macro) => macro.name);
50
+ if (!macros.length)
51
+ return value;
52
+ return (0, headerMacros_1.removeMacrosFromHeader)(value, cfg, macros);
53
+ };
54
+ /**
55
+ * Lint rule that reports macros declared in the `<h4> SAS Macros </h4>` section
56
+ * of a file header that the file itself never invokes. The formatter removes
57
+ * them from the header.
58
+ */
59
+ exports.noUnusedMacros = {
60
+ type: LintRuleType_1.LintRuleType.File,
61
+ name,
62
+ description,
63
+ message,
64
+ test,
65
+ fix
66
+ };
67
+ //# sourceMappingURL=noUnusedMacros.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noUnusedMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUnusedMacros.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AAEpD,2DAAuD;AACvD,mDAA+C;AAC/C,2DAIiC;AACjC,iEAA6D;AAE7D,MAAM,IAAI,GAAG,gBAAgB,CAAA;AAC7B,MAAM,WAAW,GACf,yFAAyF,CAAA;AAC3F,MAAM,OAAO,GAAG,2FAA2F,CAAA;AAE3G;;;;GAIG;AACH,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,MAAkB,EAAiB,EAAE;IAC3E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,IAAI,GAAG,IAAI,GAAG,CAClB,IAAA,iCAAe,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CACtE,CAAA;IAED,MAAM,MAAM,GAAkB,EAAE,CAAA;IAChC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAA;IAElC,IAAA,8BAAe,EAAC,KAAK,EAAE,MAAM,CAAC;SAC3B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,WAAW,CAAC;SAChD,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;QACpC,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAM;QAC7B,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAM;QAEzB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACpB,CAAC,CAAC,CAAA;IAEJ,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAgB,EAAE;IAChE,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,kBAAU,EAAE,CAAA;IACtC,MAAM,QAAQ,GAAG,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,mBAAQ,CAAC,OAAO,CAAA;IAE5D,OAAO,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC;QAC/C,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;QAC1C,eAAe,EAAE,KAAK,CAAC,eAAe;QACtC,QAAQ;KACT,CAAC,CAAC,CAAA;AACL,CAAC,CAAA;AAED,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAU,EAAE;IACzD,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,kBAAU,EAAE,CAAA;IACtC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAErE,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IAEhC,OAAO,IAAA,qCAAsB,EAAC,KAAK,EAAE,GAAG,EAAE,MAAM,CAAC,CAAA;AACnD,CAAC,CAAA;AAED;;;;GAIG;AACU,QAAA,cAAc,GAAiB;IAC1C,IAAI,EAAE,2BAAY,CAAC,IAAI;IACvB,IAAI;IACJ,WAAW;IACX,OAAO;IACP,IAAI;IACJ,GAAG;CACJ,CAAA"}
@@ -7,7 +7,7 @@
7
7
  "default": {
8
8
  "noEncodedPasswords": true,
9
9
  "hasDoxygenHeader": true,
10
- "defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/",
10
+ "defaultHeader": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/",
11
11
  "hasMacroNameInMend": false,
12
12
  "hasMacroParentheses": true,
13
13
  "indentationMultiple": 2,
@@ -21,6 +21,8 @@
21
21
  "noSpacesInFileNames": true,
22
22
  "noTabs": true,
23
23
  "noTrailingSpaces": true,
24
+ "noUndeclaredMacros": true,
25
+ "noUnusedMacros": true,
24
26
  "lineEndings": "off",
25
27
  "strictMacroDefinition": true,
26
28
  "ignoreList": ["sajsbuild", "sasjsresults"],
@@ -44,6 +46,8 @@
44
46
  "hasMacroNameInMend": true,
45
47
  "noNestedMacros": true,
46
48
  "noSingleAsteriskComments": false,
49
+ "noUndeclaredMacros": true,
50
+ "noUnusedMacros": true,
47
51
  "hasMacroParentheses": true,
48
52
  "lineEndings": "crlf",
49
53
  "strictMacroDefinition": true,
@@ -74,7 +78,7 @@
74
78
  "type": "string",
75
79
  "title": "defaultHeader",
76
80
  "description": "This sets the default program header - applies when a SAS program does NOT begin with `/**`.",
77
- "default": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/",
81
+ "default": "/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/",
78
82
  "examples": []
79
83
  },
80
84
  "noGremlins": {
@@ -193,6 +197,22 @@
193
197
  "default": true,
194
198
  "examples": [true, false]
195
199
  },
200
+ "noUndeclaredMacros": {
201
+ "$id": "#/properties/noUndeclaredMacros",
202
+ "type": "boolean",
203
+ "title": "noUndeclaredMacros",
204
+ "description": "Reports a warning for each macro a file invokes without declaring it in the `<h4> SAS Macros </h4>` or `<h4> Other Macros </h4>` section of the header. Macros that ship with SAS are always considered declared. The formatter resolves the warning by adding the macro names to the header, sorted alphabetically.",
205
+ "default": true,
206
+ "examples": [true, false]
207
+ },
208
+ "noUnusedMacros": {
209
+ "$id": "#/properties/noUnusedMacros",
210
+ "type": "boolean",
211
+ "title": "noUnusedMacros",
212
+ "description": "Reports a warning for each macro declared in the `<h4> SAS Macros </h4>` section of the header that the file itself never invokes. The formatter removes the unused entries. A macro can be referenced indirectly, so the warning is advisory.",
213
+ "default": true,
214
+ "examples": [true, false]
215
+ },
196
216
  "lineEndings": {
197
217
  "$id": "#/properties/lineEndings",
198
218
  "type": "string",
@@ -344,6 +364,20 @@
344
364
  "enum": ["error", "warn"],
345
365
  "default": "warn"
346
366
  },
367
+ "noUndeclaredMacros": {
368
+ "$id": "#/properties/severityLevel/noUndeclaredMacros",
369
+ "title": "noUndeclaredMacros",
370
+ "type": "string",
371
+ "enum": ["error", "warn"],
372
+ "default": "warn"
373
+ },
374
+ "noUnusedMacros": {
375
+ "$id": "#/properties/severityLevel/noUnusedMacros",
376
+ "title": "noUnusedMacros",
377
+ "type": "string",
378
+ "enum": ["error", "warn"],
379
+ "default": "warn"
380
+ },
347
381
  "lineEndings": {
348
382
  "$id": "#/properties/severityLevel/lineEndings",
349
383
  "title": "lineEndings",
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Generates `src/utils/sasMacros.generated.ts` from the SAS language data.
3
+ *
4
+ * The list of macros that ship with SAS is not lint's to maintain: it is
5
+ * language data, and `@sasjs/sas-language` owns it. This script reads the
6
+ * `macroStatements` and `macroFunctions` groups and emits the names the
7
+ * `noUndeclaredMacros` rule treats as declared.
8
+ *
9
+ * The output is committed so that the published package carries no dependency
10
+ * on the data package, and `src/utils/sasMacros.spec.ts` regenerates the list
11
+ * from the same data and fails when the two differ, so a refresh upstream
12
+ * cannot leave the constant stale.
13
+ *
14
+ * Run with `npm run generate:macros`.
15
+ */
16
+ import { readFile, writeFile } from 'node:fs/promises'
17
+ import path from 'node:path'
18
+ import { fileURLToPath } from 'node:url'
19
+
20
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
21
+ const PACKAGE = '@sasjs/sas-language'
22
+ const GROUPS = ['macroStatements', 'macroFunctions']
23
+ const TARGET = path.join(ROOT, 'src', 'utils', 'sasMacros.generated.ts')
24
+
25
+ const indexPath = (group) =>
26
+ path.join(
27
+ ROOT,
28
+ 'node_modules',
29
+ ...PACKAGE.split('/'),
30
+ 'data',
31
+ `${group}.index.json`
32
+ )
33
+
34
+ /**
35
+ * Every macro name a group carries, as a bare lower-case name. Compound
36
+ * entries such as `%DO %UNTIL` are dropped: they are a spelling of the
37
+ * `%DO` statement and no source token can match them.
38
+ */
39
+ const collectMacroNames = async () => {
40
+ const names = new Set()
41
+
42
+ for (const group of GROUPS) {
43
+ let parsed
44
+ try {
45
+ parsed = JSON.parse(await readFile(indexPath(group), 'utf8'))
46
+ } catch (error) {
47
+ throw new Error(
48
+ `cannot read ${PACKAGE}/data/${group}.index.json - run 'npm install' first (${error.message})`
49
+ )
50
+ }
51
+
52
+ if (!Array.isArray(parsed?.entries)) {
53
+ throw new Error(
54
+ `${PACKAGE}/data/${group}.index.json has an unexpected shape: expected { entries: [] }`
55
+ )
56
+ }
57
+
58
+ for (const entry of parsed.entries) {
59
+ const name = String(entry?.[0] ?? '')
60
+ .replace(/^%/, '')
61
+ .trim()
62
+ .toLowerCase()
63
+ if (!name || /\s/.test(name)) continue
64
+ names.add(name)
65
+ }
66
+ }
67
+
68
+ return [...names].sort()
69
+ }
70
+
71
+ /**
72
+ * The generated file's text.
73
+ */
74
+ const render = (names) =>
75
+ [
76
+ '// Generated by scripts/generate-macro-names.mjs from @sasjs/sas-language.',
77
+ '// Do not edit. Regenerate with `npm run generate:macros`.',
78
+ '',
79
+ '/**',
80
+ ' * The macro names that ship with SAS, lower-cased and without the leading',
81
+ ' * `%`, as published in the macroStatements and macroFunctions groups of',
82
+ ' * `@sasjs/sas-language`.',
83
+ ' */',
84
+ 'export const sasMacros: readonly string[] = [',
85
+ ...names.map((name) => ` '${name}',`),
86
+ ']',
87
+ ''
88
+ ].join('\n')
89
+
90
+ const main = async () => {
91
+ const names = await collectMacroNames()
92
+ await writeFile(TARGET, render(names))
93
+ console.log(
94
+ `written: src/utils/sasMacros.generated.ts (${names.length} names)`
95
+ )
96
+ }
97
+
98
+ main().catch((error) => {
99
+ console.error(`generation failed: ${error.message}`)
100
+ process.exit(1)
101
+ })
@@ -120,6 +120,12 @@ class LintConfig {
120
120
  if (json === null || json === void 0 ? void 0 : json.noSingleAsteriskComments) {
121
121
  this.fileLintRules.push(file_1.noSingleAsteriskComments);
122
122
  }
123
+ if ((json === null || json === void 0 ? void 0 : json.noUndeclaredMacros) !== false) {
124
+ this.fileLintRules.push(file_1.noUndeclaredMacros);
125
+ }
126
+ if ((json === null || json === void 0 ? void 0 : json.noUnusedMacros) !== false) {
127
+ this.fileLintRules.push(file_1.noUnusedMacros);
128
+ }
123
129
  if ((json === null || json === void 0 ? void 0 : json.noGremlins) !== false) {
124
130
  this.lineLintRules.push(line_1.noGremlins);
125
131
  if (json === null || json === void 0 ? void 0 : json.allowedGremlins) {
@@ -1 +1 @@
1
- {"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,7 @@ const LintConfig_1 = require("../types/LintConfig");
20
20
  const file_1 = require("@sasjs/utils/file");
21
21
  const getProjectRoot_1 = require("./getProjectRoot");
22
22
  const LineEndings_1 = require("../types/LineEndings");
23
- const getDefaultHeader = () => `/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding} <h4> SAS Macros </h4>{lineEnding}**/`;
23
+ const getDefaultHeader = () => `/**{lineEnding} @file{lineEnding} @brief <Your brief here>{lineEnding}{lineEnding} <h4> SAS Macros </h4>{lineEnding}{lineEnding}**/`;
24
24
  exports.getDefaultHeader = getDefaultHeader;
25
25
  /**
26
26
  * Default configuration that is used when a .sasjslint file is not found
@@ -1 +1 @@
1
- {"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAsCA,sCASC;AA/CD,gDAAuB;AACvB,4CAAmB;AACnB,oDAAgD;AAChD,4CAA4C;AAC5C,qDAAiD;AACjD,sDAAkD;AAE3C,MAAM,gBAAgB,GAAG,GAAG,EAAE,CACnC,gHAAgH,CAAA;AADrG,QAAA,gBAAgB,oBACqF;AAElH;;GAEG;AACU,QAAA,wBAAwB,GAAG;IACtC,WAAW,EAAE,yBAAW,CAAC,GAAG;IAC5B,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,EAAE;IACjB,mBAAmB,EAAE,EAAE;IACvB,iBAAiB,EAAE,EAAE;IACrB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,CAAC;IACtB,kBAAkB,EAAE,IAAI;IACxB,cAAc,EAAE,IAAI;IACpB,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,IAAI;IAC3B,UAAU,EAAE,IAAI;IAChB,aAAa,EAAE,IAAA,wBAAgB,GAAE;CAClC,CAAA;AAED;;;;GAIG;AACH,SAAsB,aAAa;;QACjC,MAAM,WAAW,GAAG,MAAM,IAAA,+BAAc,GAAE,CAAA;QAC1C,MAAM,gBAAgB,GAAG,WAAW,IAAI,YAAE,CAAC,OAAO,EAAE,CAAA;QACpD,MAAM,aAAa,GAAG,MAAM,IAAA,eAAQ,EAClC,cAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAC1C,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACZ,OAAO,IAAI,CAAC,SAAS,CAAC,gCAAwB,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,uBAAU,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAA;IAClD,CAAC;CAAA"}
1
+ {"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAsCA,sCASC;AA/CD,gDAAuB;AACvB,4CAAmB;AACnB,oDAAgD;AAChD,4CAA4C;AAC5C,qDAAiD;AACjD,sDAAkD;AAE3C,MAAM,gBAAgB,GAAG,GAAG,EAAE,CACnC,wIAAwI,CAAA;AAD7H,QAAA,gBAAgB,oBAC6G;AAE1I;;GAEG;AACU,QAAA,wBAAwB,GAAG;IACtC,WAAW,EAAE,yBAAW,CAAC,GAAG;IAC5B,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,kBAAkB,EAAE,IAAI;IACxB,aAAa,EAAE,EAAE;IACjB,mBAAmB,EAAE,EAAE;IACvB,iBAAiB,EAAE,EAAE;IACrB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,CAAC;IACtB,kBAAkB,EAAE,IAAI;IACxB,cAAc,EAAE,IAAI;IACpB,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,IAAI;IAC3B,UAAU,EAAE,IAAI;IAChB,aAAa,EAAE,IAAA,wBAAgB,GAAE;CAClC,CAAA;AAED;;;;GAIG;AACH,SAAsB,aAAa;;QACjC,MAAM,WAAW,GAAG,MAAM,IAAA,+BAAc,GAAE,CAAA;QAC1C,MAAM,gBAAgB,GAAG,WAAW,IAAI,YAAE,CAAC,OAAO,EAAE,CAAA;QACpD,MAAM,aAAa,GAAG,MAAM,IAAA,eAAQ,EAClC,cAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAC1C,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE;YACZ,OAAO,IAAI,CAAC,SAAS,CAAC,gCAAwB,CAAC,CAAA;QACjD,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,uBAAU,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAA;IAClD,CAAC;CAAA"}
@@ -0,0 +1,51 @@
1
+ import { LintConfig } from '../types';
2
+ /**
3
+ * A macro that is declared in the header of a file, along with the position of
4
+ * its `@li` entry.
5
+ */
6
+ export interface HeaderMacro {
7
+ name: string;
8
+ lineNumber: number;
9
+ startColumnNumber: number;
10
+ endColumnNumber: number;
11
+ section: 'sasMacros' | 'otherMacros';
12
+ }
13
+ /**
14
+ * Reads the macros declared in the header of a file - both the
15
+ * `<h4> SAS Macros </h4>` and the `<h4> Other Macros </h4>` sections.
16
+ * @param {string} text - the text content of the file.
17
+ * @param {LintConfig} config - the lint configuration, used for line endings.
18
+ * @returns {HeaderMacro[]} the declared macros, in the order they appear.
19
+ */
20
+ export declare const getHeaderMacros: (text: string, config?: LintConfig) => HeaderMacro[];
21
+ /**
22
+ * Adds macro names to the `<h4> SAS Macros </h4>` section of a file header,
23
+ * merging them with the entries that are already listed, de-duplicating by
24
+ * name, and sorting the result alphabetically. A new section is added when the
25
+ * header has none.
26
+ *
27
+ * A blank line separates the section from the header content before it and from
28
+ * whatever follows the list, so the section reads as its own block.
29
+ *
30
+ * @param {string} text - the text content of the file.
31
+ * @param {LintConfig} config - the lint configuration, used for line endings.
32
+ * @param {string[]} macros - the macro names to add.
33
+ * @returns {string} the updated text content.
34
+ */
35
+ export declare const addMacrosToHeader: (text: string, config: LintConfig, macros: string[]) => string;
36
+ /**
37
+ * Removes macro names from the `<h4> SAS Macros </h4>` section of a file
38
+ * header. The section header itself is kept, so an empty list remains a valid
39
+ * place for the formatter to add entries. The blank line before the section and
40
+ * after the list is preserved.
41
+ *
42
+ * The file is returned unchanged when it has no Doxygen header, when the header
43
+ * has no `SAS Macros` section, when the section cannot be rewritten safely, or
44
+ * when none of the named macros are listed.
45
+ *
46
+ * @param {string} text - the text content of the file.
47
+ * @param {LintConfig} config - the lint configuration, used for line endings.
48
+ * @param {string[]} macros - the macro names to remove.
49
+ * @returns {string} the updated text content.
50
+ */
51
+ export declare const removeMacrosFromHeader: (text: string, config: LintConfig, macros: string[]) => string;