@sasjs/lint 5.1.0 → 5.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 CHANGED
@@ -161,9 +161,9 @@ If creating a new value, use `{lineEnding}` instead of `\n`, eg as follows:
161
161
 
162
162
  ### hasDoxygenHeader
163
163
 
164
- The SASjs framework recommends the use of Doxygen headers for describing all types of SAS program. This check identifies files where a Doxygen header does not begin on the first line, where it opens with a single asterisk, and where it closes with one.
164
+ The SASjs framework recommends the use of Doxygen headers for describing all types of SAS program. This check identifies files where a Doxygen header does not begin on the first line, where it opens with a single asterisk, where it closes with one, where the close shares its line with the header's content, and where the block is never closed. A header is located by its close: the line that holds nothing but `**/`, which is the form `sasjs compile` and this rule set read. A block opened with `/**` whose close sits at the end of a content line, or which has no close at all, is therefore not yet a header.
165
165
 
166
- The closing half matters beyond style. This rule set and `sasjs compile` both locate the header's end by its `**/` terminator, so a header closed with a plain `*/` has no end they can find - a comment inside such a header can truncate it without being reported by [`noNestedCommentsInHeader`](#nonestedcommentsinheader). The fix widens the close marker to `**/`, which puts the header back where that check can see it. A header whose body already holds a comment opener is left alone: the first close marker may belong to that comment, and that decision belongs to the author.
166
+ The closing half matters beyond style. This rule set and `sasjs compile` both locate the header's end by its `**/` terminator, so a header closed with a plain `*/` has no end they can find - a comment inside such a header can truncate it without being reported by [`noNestedCommentsInHeader`](#nonestedcommentsinheader). The fix widens the close marker to `**/`, and moves a close that shares its line onto a line of its own, which puts the header back where that check can see it. The repairs are applied in sequence inside the rule's single fix call, so a header carrying more than one defect - `/* c */` needs its opener widened, its close widened and then moved - reaches the Doxygen form in one `sasjs lint fix` run. An unclosed block is reported but not rewritten: closing it at the end of the file would swallow whatever the author meant to keep outside it. A header whose body already holds a comment opener is left alone: the first close marker may belong to that comment, and that decision belongs to the author.
167
167
 
168
168
  - Default: true
169
169
  - Severity: WARNING
@@ -487,7 +487,7 @@ This will highlight lines with trailing spaces. Trailing spaces serve no useful
487
487
 
488
488
  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 in the `<h4> SAS Macros </h4>` section. That is the only header list `sasjs compile` reads, so it is the only one that makes a macro available to the job; entries anywhere else in the header do not count.
489
489
 
490
- The rule only looks at a file that has a header. A file with no header has no `<h4> SAS Macros </h4>` section to declare a macro in, and the formatter cannot create one, so its calls are not reported - `hasDoxygenHeader` reports the missing header, and that is the change the file needs first. A file whose header is a single-asterisk `/* ... */` block does count as having one: `hasDoxygenHeader` asks for the Doxygen form, its fix widens the block, and the declarations are added in the same `sasjs lint fix` pass.
490
+ The rule only looks at a file that has a Doxygen header: a block comment that opens with `/**` and closes with a line holding nothing but `**/`. A file with no header has no `<h4> SAS Macros </h4>` section to declare a macro in, and the formatter cannot create one, so its calls are not reported - `hasDoxygenHeader` reports the missing header, and that is the change the file needs first. A leading comment that is not a header is skipped rather than reported against a fix that cannot write: that covers a comment which fits on one line, one opened with a single asterisk (`/* some comment */`), and a `/**` block whose close shares its line or never arrives. `hasDoxygenHeader` reports each of those shapes, and its fixes put the header into the form this rule can write into earlier in the same `sasjs lint fix` pass; an unclosed block has no safe automatic repair, so it is left for the author and the declarations are checked once it closes.
491
491
 
492
492
  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 - list them in [`ignoredMacros`](#ignoredmacros), which carries `webout` by default.
493
493
 
package/package.json CHANGED
@@ -93,7 +93,7 @@
93
93
  "glob": "^14.0.2",
94
94
  "minimatch": "^11.0.1"
95
95
  },
96
- "version": "5.1.0",
96
+ "version": "5.1.1",
97
97
  "devEngines": {
98
98
  "runtime": {
99
99
  "name": "node",
@@ -13,29 +13,37 @@ const description = 'Enforce a Doxygen header at the start of each file, in the
13
13
  const message = 'File missing Doxygen header';
14
14
  const messageForSingleAsterisk = 'File not following Doxygen header style, use double asterisks';
15
15
  const messageForSingleAsteriskClose = 'File not following Doxygen header style, close with double asterisks';
16
+ const messageForCloseNotOnOwnLine = 'File not following Doxygen header style, close with the marker on a line of its own';
17
+ const messageForUnclosedHeader = 'File not following Doxygen header style, header is never closed';
16
18
  const CLOSE = '*/';
17
19
  /**
18
- * Finds a header that opens in the Doxygen form but closes with a single
19
- * asterisk, which is the closer half of the same style the opening check
20
- * enforces.
20
+ * Locates the close of a header that opens at `start`.
21
21
  *
22
22
  * A header that holds a comment opener of its own is left alone: the first
23
23
  * close marker may belong to that comment rather than to the header, and that
24
- * ambiguity belongs to the author, not to a formatter.
25
- *
26
- * @returns the index of the close marker to widen, or `null`.
24
+ * ambiguity belongs to the author, not to a formatter. That is the only case
25
+ * that answers `null`.
27
26
  */
28
- const singleAsteriskClose = (value, start) => {
27
+ const findHeaderClose = (value, start) => {
29
28
  const close = value.indexOf(CLOSE, start + 2);
30
29
  if (close === -1)
31
- return null;
32
- // Already closed in the Doxygen form.
33
- if (value[close - 1] === '*')
34
- return null;
30
+ return { kind: 'none' };
35
31
  // A comment opener before the close makes the extent ambiguous.
36
32
  if (value.slice(start + 2, close).includes('/*'))
37
33
  return null;
38
- return close;
34
+ // A close written with one asterisk, which the fix widens.
35
+ if (value[close - 1] !== '*')
36
+ return { kind: 'single', index: close };
37
+ // `close` points at the second asterisk of the marker. The line holds nothing
38
+ // but the marker when the text before its first asterisk and after its slash
39
+ // is whitespace only.
40
+ const lineStart = value.lastIndexOf('\n', close - 1) + 1;
41
+ const lineEnd = value.indexOf('\n', close);
42
+ const before = value.slice(lineStart, close - 1);
43
+ const after = value.slice(close + 2, lineEnd === -1 ? value.length : lineEnd);
44
+ return before.trim() === '' && after.trim() === ''
45
+ ? { kind: 'own-line', index: close }
46
+ : { kind: 'inline', index: close };
39
47
  };
40
48
  const diagnose = (value, config) => {
41
49
  var _a;
@@ -43,17 +51,32 @@ const diagnose = (value, config) => {
43
51
  try {
44
52
  const start = value.search(/\S/);
45
53
  if (value.trimStart().startsWith('/**')) {
46
- const close = singleAsteriskClose(value, start);
47
- if (close === null)
54
+ const close = findHeaderClose(value, start);
55
+ // The block's extent is ambiguous - leave it to the author.
56
+ if (close === null || close.kind === 'own-line')
48
57
  return null;
49
- const { lineNumber, columnNumber } = (0, getPosition_1.getPosition)(value, close);
58
+ if (close.kind === 'none') {
59
+ const { lineNumber, columnNumber } = (0, getPosition_1.getPosition)(value, start);
60
+ return {
61
+ message: messageForUnclosedHeader,
62
+ lineNumber,
63
+ startColumnNumber: columnNumber,
64
+ endColumnNumber: columnNumber + 3,
65
+ severity,
66
+ close: null
67
+ };
68
+ }
69
+ const { lineNumber, columnNumber } = (0, getPosition_1.getPosition)(value, close.index);
70
+ const inline = close.kind === 'inline';
50
71
  return {
51
- message: messageForSingleAsteriskClose,
72
+ message: inline
73
+ ? messageForCloseNotOnOwnLine
74
+ : messageForSingleAsteriskClose,
52
75
  lineNumber,
53
76
  startColumnNumber: columnNumber,
54
- endColumnNumber: columnNumber + 1,
77
+ endColumnNumber: columnNumber + (inline ? 2 : 1),
55
78
  severity,
56
- close
79
+ close: close.index
57
80
  };
58
81
  }
59
82
  const hasFileHeaderWithSingleAsterisk = value.trimStart().startsWith('/*');
@@ -64,7 +87,7 @@ const diagnose = (value, config) => {
64
87
  startColumnNumber: 1,
65
88
  endColumnNumber: 1,
66
89
  severity,
67
- close: -1
90
+ close: null
68
91
  };
69
92
  return {
70
93
  message,
@@ -72,7 +95,7 @@ const diagnose = (value, config) => {
72
95
  startColumnNumber: 1,
73
96
  endColumnNumber: 1,
74
97
  severity,
75
- close: -1
98
+ close: null
76
99
  };
77
100
  }
78
101
  catch (e) {
@@ -82,7 +105,7 @@ const diagnose = (value, config) => {
82
105
  startColumnNumber: 1,
83
106
  endColumnNumber: 1,
84
107
  severity,
85
- close: -1
108
+ close: null
86
109
  };
87
110
  }
88
111
  };
@@ -100,23 +123,80 @@ const test = (value, config) => {
100
123
  }
101
124
  ];
102
125
  };
103
- const fix = (value, config) => {
104
- const diagnosis = diagnose(value, config);
105
- if (!diagnosis)
106
- return value;
126
+ /**
127
+ * Puts a close marker on a line of its own, moving any text that followed the
128
+ * marker onto the next line. A close that overlaps the opener - an empty
129
+ * comment - has no body to keep, so the whole block is rewritten.
130
+ *
131
+ * `close` is the index of the second asterisk of the marker, as `indexOf` finds
132
+ * it, so the marker itself starts one character earlier.
133
+ */
134
+ const expandClose = (value, close, start) => {
135
+ const lineEnding = value.includes('\r\n') ? '\r\n' : '\n';
136
+ if (close <= start + 2) {
137
+ return `${value.slice(0, start)}/**${lineEnding}**/${value.slice(close + 2)}`;
138
+ }
139
+ const markerStart = close - 1;
140
+ const head = value.slice(0, markerStart).replace(/[ \t]+$/, '');
141
+ const lineEnd = value.indexOf('\n', close);
142
+ const after = value.slice(close + 2, lineEnd === -1 ? value.length : lineEnd);
143
+ if (after.trim() === '')
144
+ return `${head}${lineEnding}${value.slice(markerStart)}`;
145
+ const rest = value.slice(lineEnd === -1 ? value.length : lineEnd);
146
+ return `${head}${lineEnding}${value.slice(markerStart, close + 2)}${lineEnding}${after.trimStart()}${rest}`;
147
+ };
148
+ /**
149
+ * Applies the repair a diagnosis calls for, or returns the text unchanged when
150
+ * it has none. `hasDoxygenHeader` repairs one defect per call so that the
151
+ * diagnosis and the repair cannot drift apart; `fix` below keeps calling it
152
+ * until the header is clean.
153
+ */
154
+ const repair = (value, diagnosis, config) => {
107
155
  if (diagnosis.message == messageForSingleAsterisk)
108
156
  return value.replace('/*', '/**');
109
157
  if (diagnosis.message == messageForSingleAsteriskClose)
110
158
  return `${value.slice(0, diagnosis.close)}*${value.slice(diagnosis.close)}`;
111
- config = config || new types_1.LintConfig(getLintConfig_1.DefaultLintConfiguration);
112
- const lineEndingConfig = (config === null || config === void 0 ? void 0 : config.lineEndings) || LineEndings_1.LineEndings.LF;
159
+ if (diagnosis.message == messageForCloseNotOnOwnLine)
160
+ return expandClose(value, diagnosis.close, value.search(/\S/));
161
+ // An unclosed header has no safe repair: closing it at the end of the file
162
+ // would swallow whatever the author meant to keep outside it. The warning
163
+ // stays for the author to resolve.
164
+ if (diagnosis.message == messageForUnclosedHeader)
165
+ return value;
166
+ const cfg = config || new types_1.LintConfig(getLintConfig_1.DefaultLintConfiguration);
167
+ const lineEndingConfig = (cfg === null || cfg === void 0 ? void 0 : cfg.lineEndings) || LineEndings_1.LineEndings.LF;
113
168
  const lineEnding = lineEndingConfig === LineEndings_1.LineEndings.LF ? '\n' : '\r\n';
114
- return `${config === null || config === void 0 ? void 0 : config.defaultHeader.replace(/{lineEnding}/g, lineEnding)}${lineEnding}${value}`;
169
+ return `${cfg === null || cfg === void 0 ? void 0 : cfg.defaultHeader.replace(/{lineEnding}/g, lineEnding)}${lineEnding}${value}`;
170
+ };
171
+ /**
172
+ * Repairs a header until it is clean, or until a repair stops changing the
173
+ * text.
174
+ *
175
+ * One header can hold more than one defect, and the formatter calls a rule's
176
+ * `fix` once per pass - a single-line comment in the single-asterisk form needs
177
+ * the opener widened, the close widened, and then the close moved onto a line
178
+ * of its own. Repairing a single defect leaves the file warning again after the
179
+ * pass, and leaves `noUndeclaredMacros` switched off for it, so the remaining
180
+ * repairs are taken here. Each repair moves the header strictly closer to the
181
+ * target shape, and the bound stops a repair that somehow does not.
182
+ */
183
+ const fix = (value, config) => {
184
+ let repaired = value;
185
+ for (let step = 0; step < 3; step++) {
186
+ const diagnosis = diagnose(repaired, config);
187
+ if (!diagnosis)
188
+ return repaired;
189
+ const next = repair(repaired, diagnosis, config);
190
+ if (next === repaired)
191
+ return repaired;
192
+ repaired = next;
193
+ }
194
+ return repaired;
115
195
  };
116
196
  // Lint rule that checks for a Doxygen header in the double-asterisk form at the
117
- // start of a given file: the opening check, the closing check, and the fix that
118
- // widens a single-asterisk close so the truncation rule can locate the header's
119
- // end.
197
+ // start of a given file: the opening check, the closing checks (one asterisk,
198
+ // a close that shares its line, and a block that never closes), and the fixes
199
+ // that widen a single-asterisk close or move a close onto a line of its own.
120
200
  exports.hasDoxygenHeader = {
121
201
  type: LintRuleType_1.LintRuleType.File,
122
202
  name,
@@ -1 +1 @@
1
- {"version":3,"file":"hasDoxygenHeader.js","sourceRoot":"","sources":["../../../src/rules/file/hasDoxygenHeader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hasDoxygenHeader.js","sourceRoot":"","sources":["../../../src/rules/file/hasDoxygenHeader.ts"],"names":[],"mappings":";;;AAAA,uCAAoD;AACpD,yDAAqD;AAErD,2DAAuD;AACvD,mDAA+C;AAC/C,6DAAoE;AACpE,yDAAqD;AACrD,yDAAqD;AAErD,MAAM,IAAI,GAAG,kBAAkB,CAAA;AAC/B,MAAM,WAAW,GACf,8EAA8E,CAAA;AAChF,MAAM,OAAO,GAAG,6BAA6B,CAAA;AAC7C,MAAM,wBAAwB,GAC5B,+DAA+D,CAAA;AACjE,MAAM,6BAA6B,GACjC,sEAAsE,CAAA;AACxE,MAAM,2BAA2B,GAC/B,qFAAqF,CAAA;AACvF,MAAM,wBAAwB,GAC5B,iEAAiE,CAAA;AAEnE,MAAM,KAAK,GAAG,IAAI,CAAA;AA4BlB;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,KAAa,EAAsB,EAAE;IAC3E,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;IAE7C,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IAEzC,gEAAgE;IAChE,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAE7D,2DAA2D;IAC3D,IAAI,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;IAErE,8EAA8E;IAC9E,6EAA6E;IAC7E,sBAAsB;IACtB,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;IACxD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,GAAG,CAAC,CAAC,CAAA;IAChD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAE7E,OAAO,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAChD,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE;QACpC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACtC,CAAC,CAAA;AAED,MAAM,QAAQ,GAAG,CACf,KAAa,EACb,MAAmB,EACK,EAAE;;IAC1B,MAAM,QAAQ,GAAG,IAAA,yBAAW,EAAC,MAAM,EAAE,IAAI,EAAE,mBAAQ,CAAC,OAAO,CAAC,CAAA;IAE5D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;YAE3C,4DAA4D;YAC5D,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;gBAAE,OAAO,IAAI,CAAA;YAE5D,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC1B,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,IAAA,yBAAW,EAAC,KAAK,EAAE,KAAK,CAAC,CAAA;gBAE9D,OAAO;oBACL,OAAO,EAAE,wBAAwB;oBACjC,UAAU;oBACV,iBAAiB,EAAE,YAAY;oBAC/B,eAAe,EAAE,YAAY,GAAG,CAAC;oBACjC,QAAQ;oBACR,KAAK,EAAE,IAAI;iBACZ,CAAA;YACH,CAAC;YAED,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,IAAA,yBAAW,EAAC,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;YACpE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAA;YAEtC,OAAO;gBACL,OAAO,EAAE,MAAM;oBACb,CAAC,CAAC,2BAA2B;oBAC7B,CAAC,CAAC,6BAA6B;gBACjC,UAAU;gBACV,iBAAiB,EAAE,YAAY;gBAC/B,eAAe,EAAE,YAAY,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,QAAQ;gBACR,KAAK,EAAE,KAAK,CAAC,KAAK;aACnB,CAAA;QACH,CAAC;QAED,MAAM,+BAA+B,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;QAC1E,IAAI,+BAA+B;YACjC,OAAO;gBACL,OAAO,EAAE,wBAAwB;gBACjC,UAAU,EAAE,CAAC,MAAA,KAAK,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,UAAU,CAAC,mCAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;gBACvE,iBAAiB,EAAE,CAAC;gBACpB,eAAe,EAAE,CAAC;gBAClB,QAAQ;gBACR,KAAK,EAAE,IAAI;aACZ,CAAA;QAEH,OAAO;YACL,OAAO;YACP,UAAU,EAAE,CAAC;YACb,iBAAiB,EAAE,CAAC;YACpB,eAAe,EAAE,CAAC;YAClB,QAAQ;YACR,KAAK,EAAE,IAAI;SACZ,CAAA;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO;YACL,OAAO;YACP,UAAU,EAAE,CAAC;YACb,iBAAiB,EAAE,CAAC;YACpB,eAAe,EAAE,CAAC;YAClB,QAAQ;YACR,KAAK,EAAE,IAAI;SACZ,CAAA;IACH,CAAC;AACH,CAAC,CAAA;AAED,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAE,EAAE;IAClD,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAEzC,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAA;IAEzB,OAAO;QACL;YACE,OAAO,EAAE,SAAS,CAAC,OAAO;YAC1B,UAAU,EAAE,SAAS,CAAC,UAAU;YAChC,iBAAiB,EAAE,SAAS,CAAC,iBAAiB;YAC9C,eAAe,EAAE,SAAS,CAAC,eAAe;YAC1C,QAAQ,EAAE,SAAS,CAAC,QAAQ;SAC7B;KACF,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,KAAa,EAAU,EAAE;IAC1E,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA;IAEzD,IAAI,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,UAAU,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAA;IAC/E,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,GAAG,CAAC,CAAA;IAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;IAC/D,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAE7E,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QACrB,OAAO,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,CAAA;IAE1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;IAEjE,OAAO,GAAG,IAAI,GAAG,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,SAAS,EAAE,GAAG,IAAI,EAAE,CAAA;AAC7G,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CACb,KAAa,EACb,SAA0B,EAC1B,MAAmB,EACX,EAAE;IACV,IAAI,SAAS,CAAC,OAAO,IAAI,wBAAwB;QAC/C,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAEnC,IAAI,SAAS,CAAC,OAAO,IAAI,6BAA6B;QACpD,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,KAAe,CAAC,IAAI,KAAK,CAAC,KAAK,CAChE,SAAS,CAAC,KAAe,CAC1B,EAAE,CAAA;IAEL,IAAI,SAAS,CAAC,OAAO,IAAI,2BAA2B;QAClD,OAAO,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,KAAe,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;IAE1E,2EAA2E;IAC3E,0EAA0E;IAC1E,mCAAmC;IACnC,IAAI,SAAS,CAAC,OAAO,IAAI,wBAAwB;QAAE,OAAO,KAAK,CAAA;IAE/D,MAAM,GAAG,GAAG,MAAM,IAAI,IAAI,kBAAU,CAAC,wCAAwB,CAAC,CAAA;IAC9D,MAAM,gBAAgB,GAAG,CAAA,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,WAAW,KAAI,yBAAW,CAAC,EAAE,CAAA;IAC3D,MAAM,UAAU,GAAG,gBAAgB,KAAK,yBAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAA;IAEtE,OAAO,GAAG,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,aAAa,CAAC,OAAO,CAClC,eAAe,EACf,UAAU,CACX,GAAG,UAAU,GAAG,KAAK,EAAE,CAAA;AAC1B,CAAC,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,MAAmB,EAAU,EAAE;IACzD,IAAI,QAAQ,GAAG,KAAK,CAAA;IAEpB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC;QACpC,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAE5C,IAAI,CAAC,SAAS;YAAE,OAAO,QAAQ,CAAA;QAE/B,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QAChD,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,QAAQ,CAAA;QAEtC,QAAQ,GAAG,IAAI,CAAA;IACjB,CAAC;IAED,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,gFAAgF;AAChF,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAChE,QAAA,gBAAgB,GAAiB;IAC5C,IAAI,EAAE,2BAAY,CAAC,IAAI;IACvB,IAAI;IACJ,WAAW;IACX,OAAO;IACP,IAAI;IACJ,GAAG;CACJ,CAAA"}
@@ -4,8 +4,8 @@ exports.noUndeclaredMacros = void 0;
4
4
  const types_1 = require("../../types");
5
5
  const LintRuleType_1 = require("../../types/LintRuleType");
6
6
  const Severity_1 = require("../../types/Severity");
7
- const getHeaderLinesCount_1 = require("../../utils/getHeaderLinesCount");
8
7
  const headerMacros_1 = require("../../utils/headerMacros");
8
+ const isDoxygenHeader_1 = require("../../utils/isDoxygenHeader");
9
9
  const parseMacroCalls_1 = require("../../utils/parseMacroCalls");
10
10
  const parseMacros_1 = require("../../utils/parseMacros");
11
11
  const sasMacros_1 = require("../../utils/sasMacros");
@@ -22,8 +22,10 @@ const message = `Macro '{macro}' is not declared - add it to the <h4> SAS Macros
22
22
  * names the configuration lists in `ignoredMacros` - a macro such as `%webout`
23
23
  * has no file to point at.
24
24
  *
25
- * A file with no header is not scanned at all: the declaration the rule asks
26
- * for goes in the header, so a file without one has no place to put it.
25
+ * A file with no Doxygen header is not scanned at all: the declaration the rule
26
+ * asks for goes in the header, so a file without one has no place to put it,
27
+ * and a leading comment written with a single asterisk is a plain comment
28
+ * rather than a header.
27
29
  *
28
30
  * The scan leaves out the argument of `%str` / `%nrstr`: a file that masks a
29
31
  * trigger usually does so to print it, and reporting it would be a new warning
@@ -34,12 +36,19 @@ const message = `Macro '{macro}' is not declared - add it to the <h4> SAS Macros
34
36
  const getUndeclaredCalls = (value, config) => {
35
37
  if (!value)
36
38
  return [];
37
- // A file with no header has no `<h4> SAS Macros </h4>` section to declare a
38
- // macro in, and `addMacrosToHeader` cannot write into one - so a report here
39
- // would be a warning no pass could resolve. It is also the only reading that
40
- // lets `test` and `fix` agree on such a file. `hasDoxygenHeader` reports the
41
- // missing header itself, which is the change the file needs.
42
- if (!(0, getHeaderLinesCount_1.getHeaderLinesCount)(value, config))
39
+ // A file with no Doxygen header has no `<h4> SAS Macros </h4>` section to
40
+ // declare a macro in, and `addMacrosToHeader` cannot write into one - so a
41
+ // report here would be a warning no pass could resolve. It is also the only
42
+ // reading that lets `test` and `fix` agree on such a file. `hasDoxygenHeader`
43
+ // reports the missing header itself, which is the change the file needs.
44
+ //
45
+ // A comment that is not a Doxygen header - one that fits on one line, one
46
+ // opened with a single asterisk, or a `/**` block whose close is not a line
47
+ // of its own or never arrives - leaves nothing the fix can write into, and
48
+ // reading any of them as a header made this rule report invocations against a
49
+ // header it then left untouched. `hasDoxygenHeader` reports each shape, and
50
+ // its fixes put the header into the form this rule reads.
51
+ if (!(0, isDoxygenHeader_1.isDoxygenHeader)(value))
43
52
  return [];
44
53
  const declared = new Set();
45
54
  (0, parseMacros_1.parseMacros)(value, config).forEach((macro) => {
@@ -1 +1 @@
1
- {"version":3,"file":"noUndeclaredMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUndeclaredMacros.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"noUndeclaredMacros.js","sourceRoot":"","sources":["../../../src/rules/file/noUndeclaredMacros.ts"],"names":[],"mappings":"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"}
@@ -94,7 +94,7 @@
94
94
  "$id": "#/properties/hasDoxygenHeader",
95
95
  "type": "boolean",
96
96
  "title": "hasDoxygenHeader",
97
- "description": "Enforces the presence of a Doxygen header in the form of a comment block at the start of each SAS file. Shows a warning when one is absent.",
97
+ "description": "Enforces a Doxygen header at the start of each SAS file: a comment block that opens with `/**` and closes with a line holding nothing but `**/`. Shows a warning when the header is absent, when it opens or closes with a single asterisk, when the close shares its line with the header content, and when the block is never closed. The fix widens a single-asterisk marker and moves a close onto a line of its own; an unclosed block is left for the author.",
98
98
  "default": true,
99
99
  "examples": [true, false]
100
100
  },
@@ -274,7 +274,7 @@
274
274
  "$id": "#/properties/noUndeclaredMacros",
275
275
  "type": "boolean",
276
276
  "title": "noUndeclaredMacros",
277
- "description": "Reports a warning for each macro a file invokes without declaring it in the `<h4> SAS Macros </h4>` section of the header - the only header list `sasjs compile` reads. Macros that ship with SAS are always considered declared, and so are the macros listed in `ignoredMacros` (`webout` by default). The formatter resolves the warning by adding the macro names to the header, sorted alphabetically. A file with no header is not reported: it has no `<h4> SAS Macros </h4>` section, and the fix cannot write into one, so `hasDoxygenHeader` reports the missing header instead.",
277
+ "description": "Reports a warning for each macro a file invokes without declaring it in the `<h4> SAS Macros </h4>` section of the header - the only header list `sasjs compile` reads. Macros that ship with SAS are always considered declared, and so are the macros listed in `ignoredMacros` (`webout` by default). The formatter resolves the warning by adding the macro names to the header, sorted alphabetically. A file with no Doxygen header is not reported: it has no `<h4> SAS Macros </h4>` section, and the fix cannot write into one, so `hasDoxygenHeader` reports the missing header instead. The same skip covers a leading comment that is not a header - one that fits on one line, one opened with a single asterisk, or a `/**` block whose close is not a line of its own - because `hasDoxygenHeader` reports each of those shapes and its fixes put the header into the form this rule can write into.",
278
278
  "default": true,
279
279
  "examples": [true, false]
280
280
  },
package/utils/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from './getPosition';
9
9
  export * from './splitLines';
10
10
  export * from './getIndicesOf';
11
11
  export * from './getHeaderLinesCount';
12
+ export * from './isDoxygenHeader';
12
13
  export * from './getDataSectionsDetail';
13
14
  export * from './defaultBannedStrings';
14
15
  export * from './defaultIgnoredMacros';
package/utils/index.js CHANGED
@@ -25,6 +25,7 @@ __exportStar(require("./getPosition"), exports);
25
25
  __exportStar(require("./splitLines"), exports);
26
26
  __exportStar(require("./getIndicesOf"), exports);
27
27
  __exportStar(require("./getHeaderLinesCount"), exports);
28
+ __exportStar(require("./isDoxygenHeader"), exports);
28
29
  __exportStar(require("./getDataSectionsDetail"), exports);
29
30
  __exportStar(require("./defaultBannedStrings"), exports);
30
31
  __exportStar(require("./defaultIgnoredMacros"), exports);
@@ -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,gDAA6B;AAC7B,+CAA4B;AAC5B,iDAA8B;AAC9B,wDAAqC;AACrC,0DAAuC;AACvC,yDAAsC;AACtC,yDAAsC;AACtC,iDAA8B;AAC9B,gDAA6B;AAC7B,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,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,gDAA6B;AAC7B,+CAA4B;AAC5B,iDAA8B;AAC9B,wDAAqC;AACrC,oDAAiC;AACjC,0DAAuC;AACvC,yDAAsC;AACtC,yDAAsC;AACtC,iDAA8B;AAC9B,gDAA6B;AAC7B,iDAA8B;AAC9B,oDAAiC;AACjC,kDAA+B;AAC/B,sDAAmC;AACnC,oDAAiC;AACjC,8CAA2B"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Whether a file opens with a Doxygen header - a block comment that opens with
3
+ * two asterisks as the first non-whitespace text in the file, and closes with a
4
+ * line that holds nothing but the close marker.
5
+ *
6
+ * The close is what tells a header from a plain comment. A comment that fits on
7
+ * one line, a block opened with a single asterisk, and a block opened with two
8
+ * that is never closed all fail here; `hasDoxygenHeader` reports each of those
9
+ * shapes, and its fix puts the close on a line of its own.
10
+ *
11
+ * The opener is the first non-whitespace text, so a header that follows a
12
+ * statement is not one, and the first close marker ends the block - a close
13
+ * line below a block that already closed belongs to something else.
14
+ *
15
+ * @param {string} text - the text content of the file.
16
+ */
17
+ export declare const isDoxygenHeader: (text: string) => boolean;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDoxygenHeader = void 0;
4
+ /**
5
+ * The close of a Doxygen header: a line that holds nothing but the marker (two
6
+ * asterisks and a slash), so both the rules and `sasjs compile` can locate the
7
+ * header's end. Leading whitespace is allowed; anything else on the line is
8
+ * not.
9
+ */
10
+ const doxygenHeaderClose = '**/';
11
+ /**
12
+ * Whether a file opens with a Doxygen header - a block comment that opens with
13
+ * two asterisks as the first non-whitespace text in the file, and closes with a
14
+ * line that holds nothing but the close marker.
15
+ *
16
+ * The close is what tells a header from a plain comment. A comment that fits on
17
+ * one line, a block opened with a single asterisk, and a block opened with two
18
+ * that is never closed all fail here; `hasDoxygenHeader` reports each of those
19
+ * shapes, and its fix puts the close on a line of its own.
20
+ *
21
+ * The opener is the first non-whitespace text, so a header that follows a
22
+ * statement is not one, and the first close marker ends the block - a close
23
+ * line below a block that already closed belongs to something else.
24
+ *
25
+ * @param {string} text - the text content of the file.
26
+ */
27
+ const isDoxygenHeader = (text) => {
28
+ if (!text)
29
+ return false;
30
+ const lines = text.split(/\r\n|\n/);
31
+ let first = -1;
32
+ for (let index = 0; index < lines.length; index++) {
33
+ if (lines[index].trim() !== '') {
34
+ first = index;
35
+ break;
36
+ }
37
+ }
38
+ if (first === -1)
39
+ return false;
40
+ const opener = lines[first].trimStart();
41
+ if (!opener.startsWith('/**'))
42
+ return false;
43
+ // A close on the opener's own line is not a close of its own: the block ends
44
+ // there, so no later line can close it.
45
+ if (opener.includes('*/'))
46
+ return false;
47
+ for (let index = first + 1; index < lines.length; index++) {
48
+ const line = lines[index];
49
+ if (line.trim() === doxygenHeaderClose)
50
+ return true;
51
+ // The block closed earlier, on a line that is not a close of its own.
52
+ if (line.includes('*/'))
53
+ return false;
54
+ }
55
+ return false;
56
+ };
57
+ exports.isDoxygenHeader = isDoxygenHeader;
58
+ //# sourceMappingURL=isDoxygenHeader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isDoxygenHeader.js","sourceRoot":"","sources":["../../src/utils/isDoxygenHeader.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEhC;;;;;;;;;;;;;;;GAeG;AACI,MAAM,eAAe,GAAG,CAAC,IAAY,EAAW,EAAE;IACvD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;IACnC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAA;IAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAClD,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC/B,KAAK,GAAG,KAAK,CAAA;YACb,MAAK;QACP,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAA;IAE9B,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,CAAA;IACvC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAE3C,6EAA6E;IAC7E,wCAAwC;IACxC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAA;IAEvC,KAAK,IAAI,KAAK,GAAG,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAA;QAEzB,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,kBAAkB;YAAE,OAAO,IAAI,CAAA;QAEnD,sEAAsE;QACtE,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAA;IACvC,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAhCY,QAAA,eAAe,mBAgC3B"}