@sasjs/lint 4.0.0 → 4.1.1
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 +76 -1
- package/package.json +4 -2
- package/rules/file/index.d.ts +1 -0
- package/rules/file/index.js +3 -1
- package/rules/file/index.js.map +1 -1
- package/rules/file/noUnusedLibnames.d.ts +15 -0
- package/rules/file/noUnusedLibnames.js +56 -0
- package/rules/file/noUnusedLibnames.js.map +1 -0
- package/sasjslint-schema.json +25 -0
- package/types/LintConfig.d.ts +1 -0
- package/types/LintConfig.js +18 -0
- package/types/LintConfig.js.map +1 -1
- package/utils/getLintConfig.d.ts +2 -0
- package/utils/getLintConfig.js +2 -0
- package/utils/getLintConfig.js.map +1 -1
- package/utils/index.d.ts +1 -0
- package/utils/index.js +1 -0
- package/utils/index.js.map +1 -1
- package/utils/parseLibnames.d.ts +33 -0
- package/utils/parseLibnames.js +237 -0
- package/utils/parseLibnames.js.map +1 -0
package/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
28
28
|
"hasMacroParentheses": true,
|
|
29
29
|
"hasRequiredMacroOptions": false,
|
|
30
30
|
"ignoreList": [],
|
|
31
|
+
"ignoredLibnames": [],
|
|
31
32
|
"indentationMultiple": 2,
|
|
32
33
|
"lineEndings": "off",
|
|
33
34
|
"lowerCaseFileNames": true,
|
|
@@ -42,6 +43,7 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
42
43
|
"noTabs": true,
|
|
43
44
|
"noTrailingSpaces": true,
|
|
44
45
|
"noUndeclaredMacros": true,
|
|
46
|
+
"noUnusedLibnames": false,
|
|
45
47
|
"noUnusedMacros": true,
|
|
46
48
|
"requiredMacroOptions": [],
|
|
47
49
|
"severityLevel": {},
|
|
@@ -49,6 +51,8 @@ Configuration is via a `.sasjslint` file with the following structure (these are
|
|
|
49
51
|
}
|
|
50
52
|
```
|
|
51
53
|
|
|
54
|
+
Two keys do not have a section of their own. `requiredMacroOptions` is documented with [`hasRequiredMacroOptions`](#hasrequiredmacrooptions), the rule it configures. The deprecated alias `noTabIndentation` is accepted in place of `noTabs`, and is therefore not listed above - it is an alternative spelling rather than a setting of its own.
|
|
55
|
+
|
|
52
56
|
## SAS Lint Settings
|
|
53
57
|
|
|
54
58
|
Each setting can have three states:
|
|
@@ -57,7 +61,7 @@ Each setting can have three states:
|
|
|
57
61
|
- WARN - a warning is written to the log, but the return code will be 0
|
|
58
62
|
- ERROR - an error is written to the log, and the return code is 1
|
|
59
63
|
|
|
60
|
-
For more details, and the default state, see the description of each rule below. It is also possible to change whether a rule returns ERROR or WARN using the `
|
|
64
|
+
For more details, and the default state, see the description of each rule below. It is also possible to change whether a rule returns ERROR or WARN using the `severityLevel` object.
|
|
61
65
|
|
|
62
66
|
Configuring a non-zero return code (ERROR) is helpful when running `sasjs lint` as part of a git pre-commit hook. An example is available [here](https://github.com/sasjs/template_jobs/blob/main/.git-hooks/pre-commit).
|
|
63
67
|
|
|
@@ -163,6 +167,21 @@ Example
|
|
|
163
167
|
|
|
164
168
|
There may be specific files (or folders) that are not good candidates for linting. Simply list them in this array and they will be ignored. In addition, any files in the project `.gitignore` file will also be ignored.
|
|
165
169
|
|
|
170
|
+
### ignoredLibnames
|
|
171
|
+
|
|
172
|
+
The librefs that `noUnusedLibnames` leaves alone. List the librefs that a file assigns but does not use itself - because the connection is used by an autoexec, by `%include`d code, or by another file in the same flow:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"ignoredLibnames": ["outData", "TESTWORK"]
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
A single file can add to this list with the `ignoredLibnames` key of its [`@sasjslint`](#per-file-overrides) header block, which is the per-file escape hatch for the rule.
|
|
181
|
+
|
|
182
|
+
- Default: []
|
|
183
|
+
- Used by: `noUnusedLibnames`
|
|
184
|
+
|
|
166
185
|
### indentationMultiple
|
|
167
186
|
|
|
168
187
|
This will check each line to ensure that the count of leading spaces can be divided cleanly by this multiple.
|
|
@@ -332,6 +351,50 @@ The warning is resolved by adding the macro name to the header. Running `sasjs l
|
|
|
332
351
|
- Default: true
|
|
333
352
|
- Severity: WARNING
|
|
334
353
|
|
|
354
|
+
### noUnusedLibnames
|
|
355
|
+
|
|
356
|
+
Assigning a libref opens a connection, and on a remote engine that costs time, so a `LIBNAME` statement that a file never uses is worth knowing about.
|
|
357
|
+
|
|
358
|
+
This rule reports a warning for each libref a file assigns and never mentions again. A mention is any occurrence of the libref outside the `LIBNAME` statement, which covers the indirect forms SAS accepts as well as two-level names:
|
|
359
|
+
|
|
360
|
+
```sas
|
|
361
|
+
libname outData "&dirOut.";
|
|
362
|
+
libname TESTWORK "&work";
|
|
363
|
+
libname path2 "&sasjswork/path2";
|
|
364
|
+
|
|
365
|
+
libText = pathname("outData", "L"); /* the libref as a string */
|
|
366
|
+
%let dir = %sysfunc(pathname(TESTWORK)); /* the libref unquoted */
|
|
367
|
+
proc format library=path2; /* the libref as an option value */
|
|
368
|
+
options insert=(fmtsearch=(path1 path2));
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
The test is deliberately generous, because a use that is missed becomes a false positive. A `LIBNAME` statement that deassigns (`libname foo;`), clears (`libname foo clear;`) or reports (`libname foo list;`) assigns nothing, so it is never reported - and it counts as a use, since it refers to a libref assigned elsewhere. The reporting form `libname _all_ list;` names no libref. A libref built at runtime (`libname &lib;`) is not a literal, so there is nothing to check.
|
|
372
|
+
|
|
373
|
+
The rule is **off by default**. A `.sas` file is not a complete job, so a libref it assigns can be used by:
|
|
374
|
+
|
|
375
|
+
- an `autoexec.sas` that pre-assigns librefs
|
|
376
|
+
- macros, functions or `%include`d programs that the linter never sees
|
|
377
|
+
- another file in the same flow - in the SASjs framework, a secondary artefact
|
|
378
|
+
|
|
379
|
+
Enable it per project with `"noUnusedLibnames": true`, and list the librefs it should leave alone in [`ignoredLibnames`](#ignoredlibnames). An individual file can exempt its own librefs, or enable the rule for itself, in its `@sasjslint` header block:
|
|
380
|
+
|
|
381
|
+
```sas
|
|
382
|
+
/**
|
|
383
|
+
@file
|
|
384
|
+
@brief Loads the settlement feed
|
|
385
|
+
|
|
386
|
+
<h4> SAS Macros </h4>
|
|
387
|
+
@li mf_trim.sas
|
|
388
|
+
|
|
389
|
+
@sasjslint {"noUnusedLibnames": true, "ignoredLibnames": ["outData"]}
|
|
390
|
+
**/
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
There is no fix. `sasjs lint fix` leaves the file alone, because the formatter cannot see the rest of the job - removing the statement could break it, and silencing every warning into the ignore list would make the rule pointless.
|
|
394
|
+
|
|
395
|
+
- Default: false
|
|
396
|
+
- Severity: WARNING
|
|
397
|
+
|
|
335
398
|
### noUnusedMacros
|
|
336
399
|
|
|
337
400
|
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.
|
|
@@ -343,6 +406,18 @@ Running `sasjs lint fix` (or saving in the SASjs VS Code extension with `formatO
|
|
|
343
406
|
- Default: true
|
|
344
407
|
- Severity: WARNING
|
|
345
408
|
|
|
409
|
+
### strictMacroDefinition
|
|
410
|
+
|
|
411
|
+
This will check the syntax of a macro definition, and warn about two things:
|
|
412
|
+
|
|
413
|
+
- a parameter name that contains a space, as in `%macro myMacro(my var);`
|
|
414
|
+
- an option that SAS does not recognise, as in `%macro myMacro()/nonsense;`
|
|
415
|
+
|
|
416
|
+
The options SAS accepts are `CMD`, `DES`, `MINDELIMITER`, `MINOPERATOR`, `NOMINOPERATOR`, `PARMBUFF`, `SECURE`, `NOSECURE`, `STMT`, `SOURCE`, `SRC` and `STORE`.
|
|
417
|
+
|
|
418
|
+
- Default: true
|
|
419
|
+
- Severity: WARNING
|
|
420
|
+
|
|
346
421
|
## severityLevel
|
|
347
422
|
|
|
348
423
|
This setting allows the default severity to be adjusted. This is helpful when running the lint in a pipeline or git hook. Simply list the rules you would like to adjust along with the desired setting ("warn" or "error"), eg as follows:
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sasjs/lint",
|
|
3
3
|
"description": "Linting and formatting for SAS code",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=14"
|
|
6
|
+
},
|
|
4
7
|
"scripts": {
|
|
5
8
|
"test": "jest --coverage",
|
|
6
9
|
"build": "rimraf build && tsc",
|
|
7
10
|
"generate:macros": "node ./scripts/generate-macro-names.mjs",
|
|
8
|
-
"preinstall": "node checkNodeVersion",
|
|
9
11
|
"prebuild": "node checkNodeVersion",
|
|
10
12
|
"publishInit": "cp -r ./build/* . && rm -rf ./build && rm -rf ./src && rm tsconfig.json",
|
|
11
13
|
"package:lib": "npm run build && cp ./package.json ./checkNodeVersion.js build && cp README.md build && cd build && npm version \"5.0.0\" && npm pack",
|
|
@@ -72,5 +74,5 @@
|
|
|
72
74
|
"@sasjs/utils": "3.6.2",
|
|
73
75
|
"ignore": "7.0.11"
|
|
74
76
|
},
|
|
75
|
-
"version": "4.
|
|
77
|
+
"version": "4.1.1"
|
|
76
78
|
}
|
package/rules/file/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { lineEndings } from './lineEndings';
|
|
|
5
5
|
export { noNestedMacros } from './noNestedMacros';
|
|
6
6
|
export { noUndeclaredMacros } from './noUndeclaredMacros';
|
|
7
7
|
export { noUnusedMacros } from './noUnusedMacros';
|
|
8
|
+
export { noUnusedLibnames } from './noUnusedLibnames';
|
|
8
9
|
export { strictMacroDefinition } from './strictMacroDefinition';
|
|
9
10
|
export { hasRequiredMacroOptions } from './hasRequiredMacroOptions';
|
|
10
11
|
export { noSingleAsteriskComments } from './noSingleAsteriskComments';
|
package/rules/file/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedMacros = exports.noUndeclaredMacros = exports.noNestedMacros = exports.lineEndings = exports.hasMacroParentheses = exports.hasMacroNameInMend = exports.hasDoxygenHeader = void 0;
|
|
3
|
+
exports.noSingleAsteriskComments = exports.hasRequiredMacroOptions = exports.strictMacroDefinition = exports.noUnusedLibnames = 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");
|
|
@@ -15,6 +15,8 @@ var noUndeclaredMacros_1 = require("./noUndeclaredMacros");
|
|
|
15
15
|
Object.defineProperty(exports, "noUndeclaredMacros", { enumerable: true, get: function () { return noUndeclaredMacros_1.noUndeclaredMacros; } });
|
|
16
16
|
var noUnusedMacros_1 = require("./noUnusedMacros");
|
|
17
17
|
Object.defineProperty(exports, "noUnusedMacros", { enumerable: true, get: function () { return noUnusedMacros_1.noUnusedMacros; } });
|
|
18
|
+
var noUnusedLibnames_1 = require("./noUnusedLibnames");
|
|
19
|
+
Object.defineProperty(exports, "noUnusedLibnames", { enumerable: true, get: function () { return noUnusedLibnames_1.noUnusedLibnames; } });
|
|
18
20
|
var strictMacroDefinition_1 = require("./strictMacroDefinition");
|
|
19
21
|
Object.defineProperty(exports, "strictMacroDefinition", { enumerable: true, get: function () { return strictMacroDefinition_1.strictMacroDefinition; } });
|
|
20
22
|
var hasRequiredMacroOptions_1 = require("./hasRequiredMacroOptions");
|
package/rules/file/index.js.map
CHANGED
|
@@ -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,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"}
|
|
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,uDAAqD;AAA5C,oHAAA,gBAAgB,OAAA;AACzB,iEAA+D;AAAtD,8HAAA,qBAAqB,OAAA;AAC9B,qEAAmE;AAA1D,kIAAA,uBAAuB,OAAA;AAChC,uEAAqE;AAA5D,oIAAA,wBAAwB,OAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FileLintRule } from '../../types/LintRule';
|
|
2
|
+
/**
|
|
3
|
+
* Lint rule that reports each libref a file assigns and never uses.
|
|
4
|
+
*
|
|
5
|
+
* The rule is off by default. A file is not a complete job, so a libref it
|
|
6
|
+
* assigns can be used by an autoexec, by `%include`d code, or by another file
|
|
7
|
+
* in the same SASjs flow - in the framework, by a secondary artefact. Enable it
|
|
8
|
+
* per project with `noUnusedLibnames`, and list the librefs it should leave
|
|
9
|
+
* alone in `ignoredLibnames`, or in the `@sasjslint` block of one file.
|
|
10
|
+
*
|
|
11
|
+
* There is no fix. The formatter cannot see the rest of the job, so the safe
|
|
12
|
+
* correction is not knowable, and silencing every warning into the ignore list
|
|
13
|
+
* would make the rule pointless.
|
|
14
|
+
*/
|
|
15
|
+
export declare const noUnusedLibnames: FileLintRule;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.noUnusedLibnames = void 0;
|
|
4
|
+
const types_1 = require("../../types");
|
|
5
|
+
const LintRuleType_1 = require("../../types/LintRuleType");
|
|
6
|
+
const Severity_1 = require("../../types/Severity");
|
|
7
|
+
const parseLibnames_1 = require("../../utils/parseLibnames");
|
|
8
|
+
const name = 'noUnusedLibnames';
|
|
9
|
+
const description = 'Report librefs that a file assigns and never uses in that file.';
|
|
10
|
+
const message = `Libref '{libref}' is assigned but never used in this file - remove the LIBNAME statement, or list the libref in 'ignoredLibnames'`;
|
|
11
|
+
/**
|
|
12
|
+
* Collects the librefs the file assigns and never mentions, leaving out the
|
|
13
|
+
* ones the configuration lists in `ignoredLibnames`.
|
|
14
|
+
*
|
|
15
|
+
* A libref can be used by a file the linter never sees - an autoexec, an
|
|
16
|
+
* `%include`d program, or another file in the same job - which is why the rule
|
|
17
|
+
* is off by default and why the ignore list exists.
|
|
18
|
+
*/
|
|
19
|
+
const getUnused = (value, config) => {
|
|
20
|
+
if (!value)
|
|
21
|
+
return [];
|
|
22
|
+
const ignored = new Set(config.ignoredLibnames.map((libref) => libref.toLowerCase()));
|
|
23
|
+
return (0, parseLibnames_1.getUnusedLibnames)(value, config).filter(({ libref }) => !ignored.has(libref.toLowerCase()));
|
|
24
|
+
};
|
|
25
|
+
const test = (value, config) => {
|
|
26
|
+
const cfg = config || new types_1.LintConfig();
|
|
27
|
+
const severity = cfg.severityLevel[name] || Severity_1.Severity.Warning;
|
|
28
|
+
return getUnused(value, cfg).map((assignment) => ({
|
|
29
|
+
message: message.replace('{libref}', assignment.libref),
|
|
30
|
+
lineNumber: assignment.lineNumber,
|
|
31
|
+
startColumnNumber: assignment.startColumnNumber,
|
|
32
|
+
endColumnNumber: assignment.endColumnNumber,
|
|
33
|
+
severity
|
|
34
|
+
}));
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Lint rule that reports each libref a file assigns and never uses.
|
|
38
|
+
*
|
|
39
|
+
* The rule is off by default. A file is not a complete job, so a libref it
|
|
40
|
+
* assigns can be used by an autoexec, by `%include`d code, or by another file
|
|
41
|
+
* in the same SASjs flow - in the framework, by a secondary artefact. Enable it
|
|
42
|
+
* per project with `noUnusedLibnames`, and list the librefs it should leave
|
|
43
|
+
* alone in `ignoredLibnames`, or in the `@sasjslint` block of one file.
|
|
44
|
+
*
|
|
45
|
+
* There is no fix. The formatter cannot see the rest of the job, so the safe
|
|
46
|
+
* correction is not knowable, and silencing every warning into the ignore list
|
|
47
|
+
* would make the rule pointless.
|
|
48
|
+
*/
|
|
49
|
+
exports.noUnusedLibnames = {
|
|
50
|
+
type: LintRuleType_1.LintRuleType.File,
|
|
51
|
+
name,
|
|
52
|
+
description,
|
|
53
|
+
message,
|
|
54
|
+
test
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=noUnusedLibnames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"noUnusedLibnames.js","sourceRoot":"","sources":["../../../src/rules/file/noUnusedLibnames.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AAEpD,2DAAuD;AACvD,mDAA+C;AAC/C,6DAAgF;AAEhF,MAAM,IAAI,GAAG,kBAAkB,CAAA;AAC/B,MAAM,WAAW,GACf,iEAAiE,CAAA;AACnE,MAAM,OAAO,GAAG,mIAAmI,CAAA;AAEnJ;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG,CAAC,KAAa,EAAE,MAAkB,EAAuB,EAAE;IAC3E,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IAErB,MAAM,OAAO,GAAG,IAAI,GAAG,CACrB,MAAM,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAC7D,CAAA;IAED,OAAO,IAAA,iCAAiB,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAC5C,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CACnD,CAAA;AACH,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,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAChD,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC;QACvD,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,iBAAiB,EAAE,UAAU,CAAC,iBAAiB;QAC/C,eAAe,EAAE,UAAU,CAAC,eAAe;QAC3C,QAAQ;KACT,CAAC,CAAC,CAAA;AACL,CAAC,CAAA;AAED;;;;;;;;;;;;GAYG;AACU,QAAA,gBAAgB,GAAiB;IAC5C,IAAI,EAAE,2BAAY,CAAC,IAAI;IACvB,IAAI;IACJ,WAAW;IACX,OAAO;IACP,IAAI;CACL,CAAA"}
|
package/sasjslint-schema.json
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"hasMacroParentheses": true,
|
|
13
13
|
"hasRequiredMacroOptions": false,
|
|
14
14
|
"ignoreList": [],
|
|
15
|
+
"ignoredLibnames": [],
|
|
15
16
|
"indentationMultiple": 2,
|
|
16
17
|
"lineEndings": "off",
|
|
17
18
|
"lowerCaseFileNames": true,
|
|
@@ -26,6 +27,7 @@
|
|
|
26
27
|
"noTabs": true,
|
|
27
28
|
"noTrailingSpaces": true,
|
|
28
29
|
"noUndeclaredMacros": true,
|
|
30
|
+
"noUnusedLibnames": false,
|
|
29
31
|
"noUnusedMacros": true,
|
|
30
32
|
"requiredMacroOptions": [],
|
|
31
33
|
"severityLevel": {},
|
|
@@ -215,6 +217,14 @@
|
|
|
215
217
|
"default": true,
|
|
216
218
|
"examples": [true, false]
|
|
217
219
|
},
|
|
220
|
+
"noUnusedLibnames": {
|
|
221
|
+
"$id": "#/properties/noUnusedLibnames",
|
|
222
|
+
"type": "boolean",
|
|
223
|
+
"title": "noUnusedLibnames",
|
|
224
|
+
"description": "Reports a warning for each libref a file assigns with a LIBNAME statement and never uses in that file. A mention of the libref anywhere else counts as a use, including the indirect forms SAS accepts, such as `pathname(TESTWORK)`, `library=path2` and `fmtsearch=(path1 path2)`. Off by default, because a file is not a complete job - the libref can be used by an autoexec, by `%include`d code, or by another file in the same flow. There is no fix; list the librefs to leave alone in `ignoredLibnames`.",
|
|
225
|
+
"default": false,
|
|
226
|
+
"examples": [true, false]
|
|
227
|
+
},
|
|
218
228
|
"lineEndings": {
|
|
219
229
|
"$id": "#/properties/lineEndings",
|
|
220
230
|
"type": "string",
|
|
@@ -240,6 +250,14 @@
|
|
|
240
250
|
"default": [],
|
|
241
251
|
"examples": [["sasjsbuild/", "sasjsresults/"], ["sasjs/tests", "tmp/scratch.sas"]]
|
|
242
252
|
},
|
|
253
|
+
"ignoredLibnames": {
|
|
254
|
+
"$id": "#/properties/ignoredLibnames",
|
|
255
|
+
"type": "array",
|
|
256
|
+
"title": "ignoredLibnames",
|
|
257
|
+
"description": "An array of librefs that `noUnusedLibnames` leaves alone. A single file can add to this list with the `ignoredLibnames` key of its `@sasjslint` header block.",
|
|
258
|
+
"default": [],
|
|
259
|
+
"examples": [["outData", "TESTWORK"]]
|
|
260
|
+
},
|
|
243
261
|
"hasRequiredMacroOptions": {
|
|
244
262
|
"$id": "#/properties/hasRequiredMacroOptions",
|
|
245
263
|
"type": "boolean",
|
|
@@ -380,6 +398,13 @@
|
|
|
380
398
|
"enum": ["error", "warn"],
|
|
381
399
|
"default": "warn"
|
|
382
400
|
},
|
|
401
|
+
"noUnusedLibnames": {
|
|
402
|
+
"$id": "#/properties/severityLevel/noUnusedLibnames",
|
|
403
|
+
"title": "noUnusedLibnames",
|
|
404
|
+
"type": "string",
|
|
405
|
+
"enum": ["error", "warn"],
|
|
406
|
+
"default": "warn"
|
|
407
|
+
},
|
|
383
408
|
"lineEndings": {
|
|
384
409
|
"$id": "#/properties/severityLevel/lineEndings",
|
|
385
410
|
"title": "lineEndings",
|
package/types/LintConfig.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export declare class LintConfig {
|
|
|
26
26
|
[key: string]: Severity;
|
|
27
27
|
};
|
|
28
28
|
readonly requiredMacroOptions: string[];
|
|
29
|
+
readonly ignoredLibnames: string[];
|
|
29
30
|
constructor(json?: any);
|
|
30
31
|
/**
|
|
31
32
|
* Returns a new configuration with an override merged over this one, so a
|
package/types/LintConfig.js
CHANGED
|
@@ -30,6 +30,7 @@ class LintConfig {
|
|
|
30
30
|
this.defaultHeader = (0, utils_1.getDefaultHeader)();
|
|
31
31
|
this.severityLevel = {};
|
|
32
32
|
this.requiredMacroOptions = [];
|
|
33
|
+
this.ignoredLibnames = [];
|
|
33
34
|
this.source = json !== null && json !== void 0 ? json : {};
|
|
34
35
|
if (json === null || json === void 0 ? void 0 : json.ignoreList) {
|
|
35
36
|
if (Array.isArray(json.ignoreList)) {
|
|
@@ -129,6 +130,23 @@ class LintConfig {
|
|
|
129
130
|
if ((json === null || json === void 0 ? void 0 : json.noUnusedMacros) !== false) {
|
|
130
131
|
this.fileLintRules.push(file_1.noUnusedMacros);
|
|
131
132
|
}
|
|
133
|
+
// Off by default: a libref a file assigns can be used by another file.
|
|
134
|
+
if (json === null || json === void 0 ? void 0 : json.noUnusedLibnames) {
|
|
135
|
+
this.fileLintRules.push(file_1.noUnusedLibnames);
|
|
136
|
+
if (json === null || json === void 0 ? void 0 : json.ignoredLibnames) {
|
|
137
|
+
if (!Array.isArray(json.ignoredLibnames)) {
|
|
138
|
+
throw new Error(`Property "ignoredLibnames" can only be an array of strings.`);
|
|
139
|
+
}
|
|
140
|
+
json.ignoredLibnames.forEach((item) => {
|
|
141
|
+
if (typeof item === 'string') {
|
|
142
|
+
this.ignoredLibnames.push(item);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
throw new Error(`Property "ignoredLibnames" has invalid type of values. It can only contain strings.`);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
132
150
|
if ((json === null || json === void 0 ? void 0 : json.noGremlins) !== false) {
|
|
133
151
|
this.lineLintRules.push(line_1.noGremlins);
|
|
134
152
|
if (json === null || json === void 0 ? void 0 : json.allowedGremlins) {
|
package/types/LintConfig.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"LintConfig.js","sourceRoot":"","sources":["../../src/types/LintConfig.ts"],"names":[],"mappings":"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"}
|
package/utils/getLintConfig.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare const DefaultLintConfiguration: {
|
|
|
17
17
|
hasMacroParentheses: boolean;
|
|
18
18
|
hasRequiredMacroOptions: boolean;
|
|
19
19
|
ignoreList: never[];
|
|
20
|
+
ignoredLibnames: never[];
|
|
20
21
|
indentationMultiple: number;
|
|
21
22
|
lineEndings: LineEndings;
|
|
22
23
|
lowerCaseFileNames: boolean;
|
|
@@ -31,6 +32,7 @@ export declare const DefaultLintConfiguration: {
|
|
|
31
32
|
noTabs: boolean;
|
|
32
33
|
noTrailingSpaces: boolean;
|
|
33
34
|
noUndeclaredMacros: boolean;
|
|
35
|
+
noUnusedLibnames: boolean;
|
|
34
36
|
noUnusedMacros: boolean;
|
|
35
37
|
requiredMacroOptions: never[];
|
|
36
38
|
severityLevel: {};
|
package/utils/getLintConfig.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.DefaultLintConfiguration = {
|
|
|
38
38
|
hasMacroParentheses: true,
|
|
39
39
|
hasRequiredMacroOptions: false,
|
|
40
40
|
ignoreList: [],
|
|
41
|
+
ignoredLibnames: [],
|
|
41
42
|
indentationMultiple: 2,
|
|
42
43
|
lineEndings: LineEndings_1.LineEndings.OFF,
|
|
43
44
|
lowerCaseFileNames: true,
|
|
@@ -52,6 +53,7 @@ exports.DefaultLintConfiguration = {
|
|
|
52
53
|
noTabs: true,
|
|
53
54
|
noTrailingSpaces: true,
|
|
54
55
|
noUndeclaredMacros: true,
|
|
56
|
+
noUnusedLibnames: false,
|
|
55
57
|
noUnusedMacros: true,
|
|
56
58
|
requiredMacroOptions: [],
|
|
57
59
|
severityLevel: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"getLintConfig.js","sourceRoot":"","sources":["../../src/utils/getLintConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAqDA,sCASC;AA9DD,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;;;;;;;GAOG;AACU,QAAA,wBAAwB,GAAG;IACtC,eAAe,EAAE,EAAE;IACnB,aAAa,EAAE,IAAA,wBAAgB,GAAE;IACjC,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,mBAAmB,EAAE,IAAI;IACzB,uBAAuB,EAAE,KAAK;IAC9B,UAAU,EAAE,EAAE;IACd,eAAe,EAAE,EAAE;IACnB,mBAAmB,EAAE,CAAC;IACtB,WAAW,EAAE,yBAAW,CAAC,GAAG;IAC5B,kBAAkB,EAAE,IAAI;IACxB,iBAAiB,EAAE,EAAE;IACrB,mBAAmB,EAAE,EAAE;IACvB,aAAa,EAAE,EAAE;IACjB,kBAAkB,EAAE,IAAI;IACxB,UAAU,EAAE,IAAI;IAChB,cAAc,EAAE,IAAI;IACpB,wBAAwB,EAAE,KAAK;IAC/B,mBAAmB,EAAE,IAAI;IACzB,MAAM,EAAE,IAAI;IACZ,gBAAgB,EAAE,IAAI;IACtB,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,KAAK;IACvB,cAAc,EAAE,IAAI;IACpB,oBAAoB,EAAE,EAAE;IACxB,aAAa,EAAE,EAAE;IACjB,qBAAqB,EAAE,IAAI;CAC5B,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"}
|
package/utils/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export * from './getHeaderLinesCount';
|
|
|
10
10
|
export * from './getDataSectionsDetail';
|
|
11
11
|
export * from './headerMacros';
|
|
12
12
|
export * from './mergeLintConfig';
|
|
13
|
+
export * from './parseLibnames';
|
|
13
14
|
export * from './parseLintOverride';
|
|
14
15
|
export * from './parseMacroCalls';
|
|
15
16
|
export * from './sasMacros';
|
package/utils/index.js
CHANGED
|
@@ -26,6 +26,7 @@ __exportStar(require("./getHeaderLinesCount"), exports);
|
|
|
26
26
|
__exportStar(require("./getDataSectionsDetail"), exports);
|
|
27
27
|
__exportStar(require("./headerMacros"), exports);
|
|
28
28
|
__exportStar(require("./mergeLintConfig"), exports);
|
|
29
|
+
__exportStar(require("./parseLibnames"), exports);
|
|
29
30
|
__exportStar(require("./parseLintOverride"), exports);
|
|
30
31
|
__exportStar(require("./parseMacroCalls"), exports);
|
|
31
32
|
__exportStar(require("./sasMacros"), exports);
|
package/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA8B;AAC9B,kDAA+B;AAC/B,mDAAgC;AAChC,sDAAmC;AACnC,8CAA2B;AAC3B,iDAA8B;AAC9B,8CAA2B;AAC3B,iDAA8B;AAC9B,wDAAqC;AACrC,0DAAuC;AACvC,iDAA8B;AAC9B,oDAAiC;AACjC,sDAAmC;AACnC,oDAAiC;AACjC,8CAA2B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,iDAA8B;AAC9B,kDAA+B;AAC/B,mDAAgC;AAChC,sDAAmC;AACnC,8CAA2B;AAC3B,iDAA8B;AAC9B,8CAA2B;AAC3B,iDAA8B;AAC9B,wDAAqC;AACrC,0DAAuC;AACvC,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,sDAAmC;AACnC,oDAAiC;AACjC,8CAA2B"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { LintConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* A LIBNAME assignment found in a file, with the position of its libref.
|
|
4
|
+
*/
|
|
5
|
+
export interface LibnameAssignment {
|
|
6
|
+
libref: string;
|
|
7
|
+
lineNumber: number;
|
|
8
|
+
startColumnNumber: number;
|
|
9
|
+
endColumnNumber: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Finds the LIBNAME statements in a file that assign a libref.
|
|
13
|
+
*
|
|
14
|
+
* @param {string} text - the text content of the file.
|
|
15
|
+
* @param {LintConfig} config - the lint configuration. It is accepted for
|
|
16
|
+
* symmetry with the other utilities and is not used by the scan.
|
|
17
|
+
* @returns {LibnameAssignment[]} the assignments, in the order they appear.
|
|
18
|
+
*/
|
|
19
|
+
export declare const parseLibnames: (text: string, config?: LintConfig) => LibnameAssignment[];
|
|
20
|
+
/**
|
|
21
|
+
* Finds the librefs a file assigns and never mentions again.
|
|
22
|
+
*
|
|
23
|
+
* A mention is any occurrence of the libref outside a LIBNAME statement, which
|
|
24
|
+
* includes the indirect forms SAS accepts: `pathname(TESTWORK)`,
|
|
25
|
+
* `pathname("outData", "L")`, `library=path2`, `fmtsearch=(path1 path2)` and
|
|
26
|
+
* `%mfv_getcaslib(castest)`. The test is deliberately generous, because a use
|
|
27
|
+
* that is missed becomes a false positive.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} text - the text content of the file.
|
|
30
|
+
* @param {LintConfig} config - the lint configuration.
|
|
31
|
+
* @returns {LibnameAssignment[]} the assignments that look unused.
|
|
32
|
+
*/
|
|
33
|
+
export declare const getUnusedLibnames: (text: string, config?: LintConfig) => LibnameAssignment[];
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getUnusedLibnames = exports.parseLibnames = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Matches a LIBNAME statement, capturing the libref and the rest of the
|
|
6
|
+
* statement. A statement starts at the start of the file or after a `;`.
|
|
7
|
+
*/
|
|
8
|
+
const libnameStatement = /(?:^|;)\s*libname\s+([a-z_][a-z0-9_]*)([^;]*)/gi;
|
|
9
|
+
/** Matches the options that report or deassign a libref, rather than assign it. */
|
|
10
|
+
const nonAssigningOption = /^\s*(clear|list)\b/i;
|
|
11
|
+
/** Matches the reporting form, which names no libref. */
|
|
12
|
+
const allLibrefs = /^_all_$/i;
|
|
13
|
+
/**
|
|
14
|
+
* Replaces the regions that cannot hold a libref with spaces, keeping every
|
|
15
|
+
* other offset in place.
|
|
16
|
+
*
|
|
17
|
+
* Block comments, macro comments, comment statements and single-quoted strings
|
|
18
|
+
* are opaque. The content of a double-quoted string is kept, because SAS takes a
|
|
19
|
+
* libref as a string as often as a two-level name - `pathname("outData", "L")`
|
|
20
|
+
* uses the libref just as much as `outData.member` does.
|
|
21
|
+
*/
|
|
22
|
+
const blankNonCode = (text) => {
|
|
23
|
+
const chars = text.split('');
|
|
24
|
+
const length = text.length;
|
|
25
|
+
let index = 0;
|
|
26
|
+
let atStatementStart = true;
|
|
27
|
+
const blank = (from, to) => {
|
|
28
|
+
for (let at = from; at < to && at < length; at++) {
|
|
29
|
+
if (chars[at] !== '\n')
|
|
30
|
+
chars[at] = ' ';
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
while (index < length) {
|
|
34
|
+
const char = text[index];
|
|
35
|
+
if (char === '\n') {
|
|
36
|
+
index++;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Single-quoted string - opaque. `''` is an escaped quote character.
|
|
40
|
+
if (char === "'") {
|
|
41
|
+
let end = index + 1;
|
|
42
|
+
while (end < length) {
|
|
43
|
+
if (text[end] === "'") {
|
|
44
|
+
if (text[end + 1] === "'") {
|
|
45
|
+
end += 2;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
end++;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
if (text[end] === '\n')
|
|
52
|
+
break;
|
|
53
|
+
end++;
|
|
54
|
+
}
|
|
55
|
+
blank(index, end);
|
|
56
|
+
index = end;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
// Double-quoted string - its content is kept, so a libref inside it counts
|
|
60
|
+
// as a mention, but any `;` is masked. A semicolon inside a string is not a
|
|
61
|
+
// statement boundary, and leaving it in place would invent one, so a
|
|
62
|
+
// `libname` inside generated-code text would read as a real assignment.
|
|
63
|
+
// `""` is an escaped quote character.
|
|
64
|
+
if (char === '"') {
|
|
65
|
+
let end = index + 1;
|
|
66
|
+
while (end < length) {
|
|
67
|
+
if (text[end] === '"') {
|
|
68
|
+
if (text[end + 1] === '"') {
|
|
69
|
+
end += 2;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
end++;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
if (text[end] === '\n')
|
|
76
|
+
break;
|
|
77
|
+
end++;
|
|
78
|
+
}
|
|
79
|
+
for (let at = index; at < end; at++) {
|
|
80
|
+
if (chars[at] === ';')
|
|
81
|
+
chars[at] = ' ';
|
|
82
|
+
}
|
|
83
|
+
index = end;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (char === '/' && text[index + 1] === '*') {
|
|
87
|
+
const close = text.indexOf('*/', index + 2);
|
|
88
|
+
const end = close === -1 ? length : close + 2;
|
|
89
|
+
blank(index, end);
|
|
90
|
+
index = end;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (char === '%' && text[index + 1] === '*') {
|
|
94
|
+
const close = text.indexOf(';', index + 2);
|
|
95
|
+
const end = close === -1 ? length : close + 1;
|
|
96
|
+
blank(index, end);
|
|
97
|
+
index = end;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (char === ';') {
|
|
101
|
+
atStatementStart = true;
|
|
102
|
+
index++;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (/\s/.test(char)) {
|
|
106
|
+
index++;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
// Comment statement - its body is opaque up to the terminating `;`.
|
|
110
|
+
if (atStatementStart && char === '*') {
|
|
111
|
+
const close = text.indexOf(';', index + 1);
|
|
112
|
+
const end = close === -1 ? length : close + 1;
|
|
113
|
+
blank(index, end);
|
|
114
|
+
index = end;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
atStatementStart = false;
|
|
118
|
+
index++;
|
|
119
|
+
}
|
|
120
|
+
return chars.join('');
|
|
121
|
+
};
|
|
122
|
+
/** Replaces each span with spaces, keeping every other offset in place. */
|
|
123
|
+
const blankSpans = (code, spans) => {
|
|
124
|
+
const chars = code.split('');
|
|
125
|
+
spans.forEach(({ start, end }) => {
|
|
126
|
+
for (let at = start; at < end && at < chars.length; at++) {
|
|
127
|
+
if (chars[at] !== '\n')
|
|
128
|
+
chars[at] = ' ';
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return chars.join('');
|
|
132
|
+
};
|
|
133
|
+
/** The offset of the first character of each line. */
|
|
134
|
+
const lineStarts = (text) => {
|
|
135
|
+
const starts = [0];
|
|
136
|
+
for (let index = 0; index < text.length; index++) {
|
|
137
|
+
if (text[index] === '\n')
|
|
138
|
+
starts.push(index + 1);
|
|
139
|
+
}
|
|
140
|
+
return starts;
|
|
141
|
+
};
|
|
142
|
+
/** Converts an offset into a one-based line and column. */
|
|
143
|
+
const positionOf = (starts, offset) => {
|
|
144
|
+
let line = 0;
|
|
145
|
+
while (line + 1 < starts.length && starts[line + 1] <= offset)
|
|
146
|
+
line++;
|
|
147
|
+
return { lineNumber: line + 1, columnNumber: offset - starts[line] + 1 };
|
|
148
|
+
};
|
|
149
|
+
/** True when the libref appears in the code outside a LIBNAME statement. */
|
|
150
|
+
const isMentioned = (code, libref) => new RegExp(`(^|[^a-z0-9_])${libref}([^a-z0-9_]|$)`, 'i').test(code);
|
|
151
|
+
/**
|
|
152
|
+
* Finds the statements in `code` that assign a libref, with the offset of each
|
|
153
|
+
* statement.
|
|
154
|
+
*
|
|
155
|
+
* A statement assigns when it names a libref and carries options -
|
|
156
|
+
* `libname outData "&dirOut.";` assigns, while `libname outData;` deassigns,
|
|
157
|
+
* `libname outData clear;` deassigns and `libname outData list;` reports. The
|
|
158
|
+
* reporting form `libname _all_ list;` names no libref. A libref built at
|
|
159
|
+
* runtime (`libname &lib;`) is not a literal, so there is nothing to check.
|
|
160
|
+
*
|
|
161
|
+
* A libref assigned more than once is reported once, at its first assignment.
|
|
162
|
+
*/
|
|
163
|
+
const findAssignments = (text, code) => {
|
|
164
|
+
const assignments = [];
|
|
165
|
+
const spans = [];
|
|
166
|
+
const starts = lineStarts(text);
|
|
167
|
+
const reported = new Set();
|
|
168
|
+
let match;
|
|
169
|
+
libnameStatement.lastIndex = 0;
|
|
170
|
+
while ((match = libnameStatement.exec(code)) !== null) {
|
|
171
|
+
const libref = match[1];
|
|
172
|
+
const options = match[2];
|
|
173
|
+
if (allLibrefs.test(libref))
|
|
174
|
+
continue;
|
|
175
|
+
if (!options.trim() || nonAssigningOption.test(options))
|
|
176
|
+
continue;
|
|
177
|
+
// The statement runs to the `;` that closed it, or to the end of the file.
|
|
178
|
+
const close = code.indexOf(';', match.index + match[0].length);
|
|
179
|
+
const end = close === -1 ? code.length : close + 1;
|
|
180
|
+
spans.push({ start: match.index, end });
|
|
181
|
+
const key = libref.toLowerCase();
|
|
182
|
+
if (reported.has(key))
|
|
183
|
+
continue;
|
|
184
|
+
reported.add(key);
|
|
185
|
+
const offset = match.index + match[0].length - options.length - libref.length;
|
|
186
|
+
const { lineNumber, columnNumber } = positionOf(starts, offset);
|
|
187
|
+
assignments.push({
|
|
188
|
+
libref,
|
|
189
|
+
lineNumber,
|
|
190
|
+
startColumnNumber: columnNumber,
|
|
191
|
+
endColumnNumber: columnNumber + libref.length
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return { assignments, spans };
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Finds the LIBNAME statements in a file that assign a libref.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} text - the text content of the file.
|
|
200
|
+
* @param {LintConfig} config - the lint configuration. It is accepted for
|
|
201
|
+
* symmetry with the other utilities and is not used by the scan.
|
|
202
|
+
* @returns {LibnameAssignment[]} the assignments, in the order they appear.
|
|
203
|
+
*/
|
|
204
|
+
const parseLibnames = (text, config) => {
|
|
205
|
+
if (!text)
|
|
206
|
+
return [];
|
|
207
|
+
return findAssignments(text, blankNonCode(text)).assignments;
|
|
208
|
+
};
|
|
209
|
+
exports.parseLibnames = parseLibnames;
|
|
210
|
+
/**
|
|
211
|
+
* Finds the librefs a file assigns and never mentions again.
|
|
212
|
+
*
|
|
213
|
+
* A mention is any occurrence of the libref outside a LIBNAME statement, which
|
|
214
|
+
* includes the indirect forms SAS accepts: `pathname(TESTWORK)`,
|
|
215
|
+
* `pathname("outData", "L")`, `library=path2`, `fmtsearch=(path1 path2)` and
|
|
216
|
+
* `%mfv_getcaslib(castest)`. The test is deliberately generous, because a use
|
|
217
|
+
* that is missed becomes a false positive.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} text - the text content of the file.
|
|
220
|
+
* @param {LintConfig} config - the lint configuration.
|
|
221
|
+
* @returns {LibnameAssignment[]} the assignments that look unused.
|
|
222
|
+
*/
|
|
223
|
+
const getUnusedLibnames = (text, config) => {
|
|
224
|
+
if (!text)
|
|
225
|
+
return [];
|
|
226
|
+
const code = blankNonCode(text);
|
|
227
|
+
const { assignments, spans } = findAssignments(text, code);
|
|
228
|
+
if (!assignments.length)
|
|
229
|
+
return [];
|
|
230
|
+
// The assigning statements are blanked, so a libref does not count as a
|
|
231
|
+
// mention of itself. A statement that deassigns or reports a libref is left
|
|
232
|
+
// in place - it refers to a libref assigned somewhere else, so it is a mention.
|
|
233
|
+
const withoutAssignments = blankSpans(code, spans);
|
|
234
|
+
return assignments.filter(({ libref }) => !isMentioned(withoutAssignments, libref));
|
|
235
|
+
};
|
|
236
|
+
exports.getUnusedLibnames = getUnusedLibnames;
|
|
237
|
+
//# sourceMappingURL=parseLibnames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseLibnames.js","sourceRoot":"","sources":["../../src/utils/parseLibnames.ts"],"names":[],"mappings":"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"}
|