@pbiplint/core 0.1.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/LICENSE +661 -0
- package/NOTICE +29 -0
- package/README.md +34 -0
- package/dist/engine/config.d.ts +33 -0
- package/dist/engine/config.d.ts.map +1 -0
- package/dist/engine/config.js +61 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/ignore.d.ts +8 -0
- package/dist/engine/ignore.d.ts.map +1 -0
- package/dist/engine/ignore.js +17 -0
- package/dist/engine/ignore.js.map +1 -0
- package/dist/engine/lint.d.ts +38 -0
- package/dist/engine/lint.d.ts.map +1 -0
- package/dist/engine/lint.js +33 -0
- package/dist/engine/lint.js.map +1 -0
- package/dist/engine/rank.d.ts +24 -0
- package/dist/engine/rank.d.ts.map +1 -0
- package/dist/engine/rank.js +38 -0
- package/dist/engine/rank.js.map +1 -0
- package/dist/engine/run.d.ts +21 -0
- package/dist/engine/run.d.ts.map +1 -0
- package/dist/engine/run.js +43 -0
- package/dist/engine/run.js.map +1 -0
- package/dist/format/index.d.ts +11 -0
- package/dist/format/index.d.ts.map +1 -0
- package/dist/format/index.js +21 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/json.d.ts +4 -0
- package/dist/format/json.d.ts.map +1 -0
- package/dist/format/json.js +20 -0
- package/dist/format/json.js.map +1 -0
- package/dist/format/markdown.d.ts +4 -0
- package/dist/format/markdown.d.ts.map +1 -0
- package/dist/format/markdown.js +28 -0
- package/dist/format/markdown.js.map +1 -0
- package/dist/format/sarif.d.ts +4 -0
- package/dist/format/sarif.d.ts.map +1 -0
- package/dist/format/sarif.js +77 -0
- package/dist/format/sarif.js.map +1 -0
- package/dist/format/text.d.ts +31 -0
- package/dist/format/text.d.ts.map +1 -0
- package/dist/format/text.js +57 -0
- package/dist/format/text.js.map +1 -0
- package/dist/index/build.d.ts +11 -0
- package/dist/index/build.d.ts.map +1 -0
- package/dist/index/build.js +11 -0
- package/dist/index/build.js.map +1 -0
- package/dist/index/references.d.ts +37 -0
- package/dist/index/references.d.ts.map +1 -0
- package/dist/index/references.js +126 -0
- package/dist/index/references.js.map +1 -0
- package/dist/index/relationships.d.ts +10 -0
- package/dist/index/relationships.d.ts.map +1 -0
- package/dist/index/relationships.js +24 -0
- package/dist/index/relationships.js.map +1 -0
- package/dist/index/usage.d.ts +11 -0
- package/dist/index/usage.d.ts.map +1 -0
- package/dist/index/usage.js +25 -0
- package/dist/index/usage.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/model/build.d.ts +9 -0
- package/dist/model/build.d.ts.map +1 -0
- package/dist/model/build.js +282 -0
- package/dist/model/build.js.map +1 -0
- package/dist/model/names.d.ts +12 -0
- package/dist/model/names.d.ts.map +1 -0
- package/dist/model/names.js +21 -0
- package/dist/model/names.js.map +1 -0
- package/dist/model/types.d.ts +150 -0
- package/dist/model/types.d.ts.map +1 -0
- package/dist/model/types.js +2 -0
- package/dist/model/types.js.map +1 -0
- package/dist/rules/helpers.d.ts +54 -0
- package/dist/rules/helpers.d.ts.map +1 -0
- package/dist/rules/helpers.js +189 -0
- package/dist/rules/helpers.js.map +1 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +4 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/microsoft-bpa/bpa-rules.data.d.ts +12 -0
- package/dist/rules/microsoft-bpa/bpa-rules.data.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/bpa-rules.data.js +664 -0
- package/dist/rules/microsoft-bpa/bpa-rules.data.js.map +1 -0
- package/dist/rules/microsoft-bpa/columns.d.ts +14 -0
- package/dist/rules/microsoft-bpa/columns.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/columns.js +117 -0
- package/dist/rules/microsoft-bpa/columns.js.map +1 -0
- package/dist/rules/microsoft-bpa/define.d.ts +14 -0
- package/dist/rules/microsoft-bpa/define.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/define.js +75 -0
- package/dist/rules/microsoft-bpa/define.js.map +1 -0
- package/dist/rules/microsoft-bpa/dependencies.d.ts +11 -0
- package/dist/rules/microsoft-bpa/dependencies.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/dependencies.js +56 -0
- package/dist/rules/microsoft-bpa/dependencies.js.map +1 -0
- package/dist/rules/microsoft-bpa/index.d.ts +4 -0
- package/dist/rules/microsoft-bpa/index.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/index.js +20 -0
- package/dist/rules/microsoft-bpa/index.js.map +1 -0
- package/dist/rules/microsoft-bpa/live-model.d.ts +3 -0
- package/dist/rules/microsoft-bpa/live-model.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/live-model.js +10 -0
- package/dist/rules/microsoft-bpa/live-model.js.map +1 -0
- package/dist/rules/microsoft-bpa/measures.d.ts +15 -0
- package/dist/rules/microsoft-bpa/measures.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/measures.js +69 -0
- package/dist/rules/microsoft-bpa/measures.js.map +1 -0
- package/dist/rules/microsoft-bpa/naming.d.ts +14 -0
- package/dist/rules/microsoft-bpa/naming.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/naming.js +47 -0
- package/dist/rules/microsoft-bpa/naming.js.map +1 -0
- package/dist/rules/microsoft-bpa/relationships.d.ts +14 -0
- package/dist/rules/microsoft-bpa/relationships.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/relationships.js +97 -0
- package/dist/rules/microsoft-bpa/relationships.js.map +1 -0
- package/dist/rules/microsoft-bpa/tables.d.ts +16 -0
- package/dist/rules/microsoft-bpa/tables.d.ts.map +1 -0
- package/dist/rules/microsoft-bpa/tables.js +147 -0
- package/dist/rules/microsoft-bpa/tables.js.map +1 -0
- package/dist/rules/parse-issue.d.ts +4 -0
- package/dist/rules/parse-issue.d.ts.map +1 -0
- package/dist/rules/parse-issue.js +19 -0
- package/dist/rules/parse-issue.js.map +1 -0
- package/dist/rules/rule-summaries.data.d.ts +3 -0
- package/dist/rules/rule-summaries.data.d.ts.map +1 -0
- package/dist/rules/rule-summaries.data.js +78 -0
- package/dist/rules/rule-summaries.data.js.map +1 -0
- package/dist/rules/types.d.ts +42 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +15 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/tmdl/parse.d.ts +7 -0
- package/dist/tmdl/parse.d.ts.map +1 -0
- package/dist/tmdl/parse.js +196 -0
- package/dist/tmdl/parse.js.map +1 -0
- package/dist/tmdl/quote.d.ts +5 -0
- package/dist/tmdl/quote.d.ts.map +1 -0
- package/dist/tmdl/quote.js +15 -0
- package/dist/tmdl/quote.js.map +1 -0
- package/dist/tmdl/types.d.ts +32 -0
- package/dist/tmdl/types.d.ts.map +1 -0
- package/dist/tmdl/types.js +2 -0
- package/dist/tmdl/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +51 -0
- package/src/engine/config.ts +91 -0
- package/src/engine/ignore.ts +18 -0
- package/src/engine/lint.ts +79 -0
- package/src/engine/rank.ts +67 -0
- package/src/engine/run.ts +67 -0
- package/src/format/index.ts +37 -0
- package/src/format/json.ts +22 -0
- package/src/format/markdown.ts +37 -0
- package/src/format/sarif.ts +85 -0
- package/src/format/text.ts +90 -0
- package/src/index/build.ts +18 -0
- package/src/index/references.ts +170 -0
- package/src/index/relationships.ts +33 -0
- package/src/index/usage.ts +33 -0
- package/src/index.ts +80 -0
- package/src/model/build.ts +306 -0
- package/src/model/names.ts +31 -0
- package/src/model/types.ts +165 -0
- package/src/rules/helpers.ts +223 -0
- package/src/rules/index.ts +5 -0
- package/src/rules/microsoft-bpa/bpa-rules.data.ts +782 -0
- package/src/rules/microsoft-bpa/columns.ts +192 -0
- package/src/rules/microsoft-bpa/define.ts +84 -0
- package/src/rules/microsoft-bpa/dependencies.ts +95 -0
- package/src/rules/microsoft-bpa/index.ts +22 -0
- package/src/rules/microsoft-bpa/live-model.ts +10 -0
- package/src/rules/microsoft-bpa/measures.ts +143 -0
- package/src/rules/microsoft-bpa/naming.ts +104 -0
- package/src/rules/microsoft-bpa/relationships.ts +189 -0
- package/src/rules/microsoft-bpa/tables.ts +249 -0
- package/src/rules/parse-issue.ts +23 -0
- package/src/rules/rule-summaries.data.ts +144 -0
- package/src/rules/types.ts +87 -0
- package/src/tmdl/parse.ts +194 -0
- package/src/tmdl/quote.ts +15 -0
- package/src/tmdl/types.ts +34 -0
- package/src/version.ts +2 -0
package/NOTICE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
pbiplint itself is licensed under the GNU Affero General Public License,
|
|
2
|
+
version 3 or later; see LICENSE. The MIT license text below applies only
|
|
3
|
+
to the third-party material described here, not to pbiplint.
|
|
4
|
+
|
|
5
|
+
pbiplint bundles rule definitions derived from BPARules.json in the
|
|
6
|
+
microsoft/Analysis-Services repository (https://github.com/microsoft/Analysis-Services),
|
|
7
|
+
which is licensed under the MIT License:
|
|
8
|
+
|
|
9
|
+
MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @pbiplint/core
|
|
2
|
+
|
|
3
|
+
The linter behind [pbiplint](https://pbiplint.com): parse TMDL, build the semantic model, run the
|
|
4
|
+
best-practice rules, rank the findings, and format them. Pure TypeScript with no dependencies, no
|
|
5
|
+
Node APIs, and no network access, so it runs in a browser tab as well as in Node 20 or later.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { formatText, lint } from "@pbiplint/core";
|
|
9
|
+
|
|
10
|
+
const result = lint([
|
|
11
|
+
{ path: "definition/model.tmdl", text: modelTmdl },
|
|
12
|
+
{ path: "definition/tables/Sales.tmdl", text: salesTmdl },
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
console.log(formatText(result)); // or formatJson, formatMarkdown, formatSarif
|
|
16
|
+
for (const group of result.groups) console.log(group.rule.id, group.findings.length, group.rule.url);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`lint(files, { config })` takes the files of one model with paths relative to the model root and an
|
|
20
|
+
optional `pbiplint.config.json` object. `result.groups` is ranked by severity, then category, then
|
|
21
|
+
count, exactly as the command line and the website show it. Every rule has a page at
|
|
22
|
+
`https://pbiplint.com/rules/<slug>`; `group.rule.url` points at it.
|
|
23
|
+
|
|
24
|
+
The rules are literal ports of the Microsoft Best Practice Analyzer ruleset, verified against
|
|
25
|
+
Tabular Editor. Quirks are kept on purpose and documented on each rule's page.
|
|
26
|
+
|
|
27
|
+
For the command line, install [`pbiplint`](https://www.npmjs.com/package/pbiplint). Source, issues,
|
|
28
|
+
and contributing: https://github.com/pbiplint/pbiplint.
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
Copyright (C) 2026 McKinley Consulting. GNU Affero General Public License, version 3 or later; see
|
|
33
|
+
LICENSE. The vendored Microsoft ruleset is MIT-licensed; see NOTICE. The name pbiplint and its
|
|
34
|
+
logo are trademarks of McKinley Consulting.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Rule, Severity } from "../rules/types.js";
|
|
2
|
+
export type SeverityName = "info" | "warning" | "error";
|
|
3
|
+
/** Shape of pbiplint.config.json. */
|
|
4
|
+
export interface PbiplintConfig {
|
|
5
|
+
/** Optional JSON Schema URL, so an editor can validate and complete the file. Ignored here. */
|
|
6
|
+
$schema?: string;
|
|
7
|
+
/** Per rule: "off" disables it; a severity name overrides its severity. */
|
|
8
|
+
rules?: Record<string, "off" | SeverityName>;
|
|
9
|
+
/** Lowest severity that makes the CLI exit nonzero. Default "error". */
|
|
10
|
+
failOn?: SeverityName | "none";
|
|
11
|
+
}
|
|
12
|
+
export interface ResolvedConfig {
|
|
13
|
+
disabled: Set<string>;
|
|
14
|
+
severity: Map<string, Severity>;
|
|
15
|
+
failOn: Severity | null;
|
|
16
|
+
}
|
|
17
|
+
export declare class ConfigError extends Error {
|
|
18
|
+
}
|
|
19
|
+
export interface BoundConfig {
|
|
20
|
+
/** The same settings keyed by the rules' own ids. */
|
|
21
|
+
config: ResolvedConfig;
|
|
22
|
+
/** Ids from the config that match no rule, as written. */
|
|
23
|
+
unknownRules: string[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Map the config's rule ids onto `rules` without regard to case, so `hide_foreign_keys` reaches
|
|
27
|
+
* HIDE_FOREIGN_KEYS, and list the ids that reach nothing so a typo never disables a rule silently.
|
|
28
|
+
*/
|
|
29
|
+
export declare function bindConfig(config: ResolvedConfig, rules: Rule[]): BoundConfig;
|
|
30
|
+
export declare const SEVERITY_BY_NAME: Record<SeverityName, Severity>;
|
|
31
|
+
export declare function isResolvedConfig(v: unknown): v is ResolvedConfig;
|
|
32
|
+
export declare function resolveConfig(raw?: unknown): ResolvedConfig;
|
|
33
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/engine/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAExD,qCAAqC;AACrC,MAAM,WAAW,cAAc;IAC7B,+FAA+F;IAC/F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,YAAY,CAAC,CAAC;IAC7C,wEAAwE;IACxE,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChC,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzB;AAED,qBAAa,WAAY,SAAQ,KAAK;CAAG;AAEzC,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,MAAM,EAAE,cAAc,CAAC;IACvB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,WAAW,CAe7E;AAED,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAqC,CAAC;AAKlG,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,cAAc,CAEhE;AAED,wBAAgB,aAAa,CAAC,GAAG,GAAE,OAAY,GAAG,cAAc,CA+B/D"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export class ConfigError extends Error {
|
|
2
|
+
}
|
|
3
|
+
/**
|
|
4
|
+
* Map the config's rule ids onto `rules` without regard to case, so `hide_foreign_keys` reaches
|
|
5
|
+
* HIDE_FOREIGN_KEYS, and list the ids that reach nothing so a typo never disables a rule silently.
|
|
6
|
+
*/
|
|
7
|
+
export function bindConfig(config, rules) {
|
|
8
|
+
const idByUpper = new Map(rules.map((r) => [r.id.toUpperCase(), r.id]));
|
|
9
|
+
const bound = { disabled: new Set(), severity: new Map(), failOn: config.failOn };
|
|
10
|
+
const unknownRules = [];
|
|
11
|
+
for (const id of config.disabled) {
|
|
12
|
+
const real = idByUpper.get(id.toUpperCase());
|
|
13
|
+
if (real === undefined)
|
|
14
|
+
unknownRules.push(id);
|
|
15
|
+
else
|
|
16
|
+
bound.disabled.add(real);
|
|
17
|
+
}
|
|
18
|
+
for (const [id, severity] of config.severity) {
|
|
19
|
+
const real = idByUpper.get(id.toUpperCase());
|
|
20
|
+
if (real === undefined)
|
|
21
|
+
unknownRules.push(id);
|
|
22
|
+
else
|
|
23
|
+
bound.severity.set(real, severity);
|
|
24
|
+
}
|
|
25
|
+
return { config: bound, unknownRules };
|
|
26
|
+
}
|
|
27
|
+
export const SEVERITY_BY_NAME = { info: 1, warning: 2, error: 3 };
|
|
28
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
29
|
+
export function isResolvedConfig(v) {
|
|
30
|
+
return isRecord(v) && v.disabled instanceof Set && v.severity instanceof Map;
|
|
31
|
+
}
|
|
32
|
+
export function resolveConfig(raw = {}) {
|
|
33
|
+
if (!isRecord(raw))
|
|
34
|
+
throw new ConfigError("pbiplint.config.json must be a JSON object");
|
|
35
|
+
for (const k of Object.keys(raw))
|
|
36
|
+
if (k !== "rules" && k !== "failOn" && k !== "$schema")
|
|
37
|
+
throw new ConfigError(`pbiplint.config.json: unknown key "${k}"`);
|
|
38
|
+
const out = { disabled: new Set(), severity: new Map(), failOn: 3 };
|
|
39
|
+
if (raw.rules !== undefined) {
|
|
40
|
+
if (!isRecord(raw.rules))
|
|
41
|
+
throw new ConfigError('pbiplint.config.json: "rules" must be an object of rule id to "off" | "info" | "warning" | "error"');
|
|
42
|
+
for (const [id, v] of Object.entries(raw.rules)) {
|
|
43
|
+
if (v === "off")
|
|
44
|
+
out.disabled.add(id);
|
|
45
|
+
else if (v === "info" || v === "warning" || v === "error")
|
|
46
|
+
out.severity.set(id, SEVERITY_BY_NAME[v]);
|
|
47
|
+
else
|
|
48
|
+
throw new ConfigError(`pbiplint.config.json: rules["${id}"] must be "off", "info", "warning", or "error"`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (raw.failOn !== undefined) {
|
|
52
|
+
if (raw.failOn === "none")
|
|
53
|
+
out.failOn = null;
|
|
54
|
+
else if (raw.failOn === "info" || raw.failOn === "warning" || raw.failOn === "error")
|
|
55
|
+
out.failOn = SEVERITY_BY_NAME[raw.failOn];
|
|
56
|
+
else
|
|
57
|
+
throw new ConfigError('pbiplint.config.json: "failOn" must be "info", "warning", "error", or "none"');
|
|
58
|
+
}
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/engine/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Named } from "../model/types.js";
|
|
2
|
+
export declare const IGNORE_ANNOTATION = "pbiplint.ignore";
|
|
3
|
+
/**
|
|
4
|
+
* True when the object carries `annotation pbiplint.ignore = RULE_A, RULE_B` naming this rule, or
|
|
5
|
+
* `= *`. Ids are matched without regard to case, so a hand-typed lowercase id still works.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isIgnored(object: Named | undefined, ruleId: string): boolean;
|
|
8
|
+
//# sourceMappingURL=ignore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.d.ts","sourceRoot":"","sources":["../../src/engine/ignore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAG/C,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAEnD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAQ5E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { unquoteValue } from "../tmdl/quote.js";
|
|
2
|
+
export const IGNORE_ANNOTATION = "pbiplint.ignore";
|
|
3
|
+
/**
|
|
4
|
+
* True when the object carries `annotation pbiplint.ignore = RULE_A, RULE_B` naming this rule, or
|
|
5
|
+
* `= *`. Ids are matched without regard to case, so a hand-typed lowercase id still works.
|
|
6
|
+
*/
|
|
7
|
+
export function isIgnored(object, ruleId) {
|
|
8
|
+
const raw = object?.annotations[IGNORE_ANNOTATION];
|
|
9
|
+
if (raw === undefined)
|
|
10
|
+
return false;
|
|
11
|
+
const wanted = ruleId.toUpperCase();
|
|
12
|
+
return unquoteValue(raw)
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((s) => s.trim())
|
|
15
|
+
.some((s) => s === "*" || s.toUpperCase() === wanted);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=ignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.js","sourceRoot":"","sources":["../../src/engine/ignore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAEnD;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,MAAyB,EAAE,MAAc;IACjE,MAAM,GAAG,GAAG,MAAM,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAC;IACnD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO,YAAY,CAAC,GAAG,CAAC;SACrB,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;AAC1D,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Model } from "../model/types.js";
|
|
2
|
+
import type { Finding, Rule } from "../rules/types.js";
|
|
3
|
+
import { type PbiplintConfig, type ResolvedConfig } from "./config.js";
|
|
4
|
+
import { type RankedGroup } from "./rank.js";
|
|
5
|
+
import { type RuleError, type SkippedRule } from "./run.js";
|
|
6
|
+
export interface LintFile {
|
|
7
|
+
/** Path relative to the model root, forward slashes, e.g. `definition/tables/Sales.tmdl`. */
|
|
8
|
+
path: string;
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export interface LintOptions {
|
|
12
|
+
config?: PbiplintConfig | ResolvedConfig;
|
|
13
|
+
rules?: Rule[];
|
|
14
|
+
}
|
|
15
|
+
export interface LintSummary {
|
|
16
|
+
files: number;
|
|
17
|
+
findings: number;
|
|
18
|
+
errors: number;
|
|
19
|
+
warnings: number;
|
|
20
|
+
infos: number;
|
|
21
|
+
rulesRun: number;
|
|
22
|
+
rulesSkipped: SkippedRule[];
|
|
23
|
+
ruleErrors: RuleError[];
|
|
24
|
+
ignored: number;
|
|
25
|
+
/** Rule ids named in the config that match no rule, as written there. */
|
|
26
|
+
unknownRules: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface LintResult {
|
|
29
|
+
model: Model;
|
|
30
|
+
findings: Finding[];
|
|
31
|
+
groups: RankedGroup[];
|
|
32
|
+
summary: LintSummary;
|
|
33
|
+
/** True when any finding's effective severity is at or above the configured failOn. */
|
|
34
|
+
failed: boolean;
|
|
35
|
+
}
|
|
36
|
+
/** The one call the web app and the CLI both make. Pure: no I/O, no network. */
|
|
37
|
+
export declare function lint(files: LintFile[], options?: LintOptions): LintResult;
|
|
38
|
+
//# sourceMappingURL=lint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/engine/lint.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EACpB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAQ,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AAEtE,MAAM,WAAW,QAAQ;IACvB,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,cAAc,GAAG,cAAc,CAAC;IACzC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,WAAW,CAAC;IACrB,uFAAuF;IACvF,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,gFAAgF;AAChF,wBAAgB,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,GAAE,WAAgB,GAAG,UAAU,CA2B7E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { buildIndexes } from "../index/build.js";
|
|
2
|
+
import { buildModel } from "../model/build.js";
|
|
3
|
+
import { defaultRules } from "../rules/index.js";
|
|
4
|
+
import { parseTmdl } from "../tmdl/parse.js";
|
|
5
|
+
import { bindConfig, isResolvedConfig, resolveConfig, } from "./config.js";
|
|
6
|
+
import { rank } from "./rank.js";
|
|
7
|
+
import { runRules } from "./run.js";
|
|
8
|
+
/** The one call the web app and the CLI both make. Pure: no I/O, no network. */
|
|
9
|
+
export function lint(files, options = {}) {
|
|
10
|
+
const rules = options.rules ?? defaultRules;
|
|
11
|
+
const { config, unknownRules } = bindConfig(isResolvedConfig(options.config) ? options.config : resolveConfig(options.config), rules);
|
|
12
|
+
const parsed = files.map((f) => parseTmdl(f.path, f.text));
|
|
13
|
+
const model = buildModel(parsed);
|
|
14
|
+
const indexes = buildIndexes(model);
|
|
15
|
+
const run = runRules(model, indexes, rules, config);
|
|
16
|
+
const groups = rank(run.findings, rules, config);
|
|
17
|
+
const count = (severity) => groups.filter((g) => g.rule.severity === severity).reduce((n, g) => n + g.findings.length, 0);
|
|
18
|
+
const summary = {
|
|
19
|
+
files: files.length,
|
|
20
|
+
findings: run.findings.length,
|
|
21
|
+
errors: count(3),
|
|
22
|
+
warnings: count(2),
|
|
23
|
+
infos: count(1),
|
|
24
|
+
rulesRun: run.rulesRun.length,
|
|
25
|
+
rulesSkipped: run.rulesSkipped,
|
|
26
|
+
ruleErrors: run.ruleErrors,
|
|
27
|
+
ignored: run.ignored,
|
|
28
|
+
unknownRules,
|
|
29
|
+
};
|
|
30
|
+
const failed = config.failOn !== null && groups.some((g) => g.rule.severity >= config.failOn);
|
|
31
|
+
return { model, findings: run.findings, groups, summary, failed };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=lint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/engine/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,aAAa,GAGd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,IAAI,EAAoB,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAoC,MAAM,UAAU,CAAC;AAoCtE,gFAAgF;AAChF,MAAM,UAAU,IAAI,CAAC,KAAiB,EAAE,UAAuB,EAAE;IAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC;IAC5C,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,CACzC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EACjF,KAAK,CACN,CAAC;IACF,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,CAAC,QAAgB,EAAE,EAAE,CACjC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAChG,MAAM,OAAO,GAAgB;QAC3B,KAAK,EAAE,KAAK,CAAC,MAAM;QACnB,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;QAC7B,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAChB,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAClB,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACf,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM;QAC7B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,YAAY;KACb,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,MAAO,CAAC,CAAC;IAC/F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;AACpE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Category, type Finding, type Rule, type RuleStatus, type Severity } from "../rules/types.js";
|
|
2
|
+
import type { ResolvedConfig } from "./config.js";
|
|
3
|
+
export interface RuleSummary {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
category: Category;
|
|
7
|
+
/** Effective severity after config overrides. */
|
|
8
|
+
severity: Severity;
|
|
9
|
+
slug: string;
|
|
10
|
+
url: string;
|
|
11
|
+
status: RuleStatus;
|
|
12
|
+
}
|
|
13
|
+
export interface RankedGroup {
|
|
14
|
+
rule: RuleSummary;
|
|
15
|
+
findings: Finding[];
|
|
16
|
+
}
|
|
17
|
+
export declare const effectiveSeverity: (rule: Rule, config: ResolvedConfig) => Severity;
|
|
18
|
+
export declare function summarizeRule(rule: Rule, config: ResolvedConfig): RuleSummary;
|
|
19
|
+
/**
|
|
20
|
+
* Group findings by rule and order the groups by severity (error first), category priority,
|
|
21
|
+
* finding count (more first), then rule id. Findings inside a group keep model order.
|
|
22
|
+
*/
|
|
23
|
+
export declare function rank(findings: Finding[], rules: Rule[], config: ResolvedConfig): RankedGroup[];
|
|
24
|
+
//# sourceMappingURL=rank.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.d.ts","sourceRoot":"","sources":["../../src/engine/rank.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,IAAI,EACT,KAAK,UAAU,EACf,KAAK,QAAQ,EACd,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,GAAI,MAAM,IAAI,EAAE,QAAQ,cAAc,KAAG,QACxB,CAAC;AAEhD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,GAAG,WAAW,CAU7E;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,cAAc,GAAG,WAAW,EAAE,CAoB9F"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ruleUrl, slug } from "../model/names.js";
|
|
2
|
+
import { CATEGORY_ORDER, } from "../rules/types.js";
|
|
3
|
+
export const effectiveSeverity = (rule, config) => config.severity.get(rule.id) ?? rule.severity;
|
|
4
|
+
export function summarizeRule(rule, config) {
|
|
5
|
+
return {
|
|
6
|
+
id: rule.id,
|
|
7
|
+
name: rule.name,
|
|
8
|
+
category: rule.category,
|
|
9
|
+
severity: effectiveSeverity(rule, config),
|
|
10
|
+
slug: slug(rule.id),
|
|
11
|
+
url: ruleUrl(rule.id),
|
|
12
|
+
status: rule.status,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Group findings by rule and order the groups by severity (error first), category priority,
|
|
17
|
+
* finding count (more first), then rule id. Findings inside a group keep model order.
|
|
18
|
+
*/
|
|
19
|
+
export function rank(findings, rules, config) {
|
|
20
|
+
const byId = new Map(rules.map((r) => [r.id, r]));
|
|
21
|
+
const groups = new Map();
|
|
22
|
+
for (const f of findings) {
|
|
23
|
+
let g = groups.get(f.ruleId);
|
|
24
|
+
if (!g) {
|
|
25
|
+
const rule = byId.get(f.ruleId);
|
|
26
|
+
if (!rule)
|
|
27
|
+
throw new Error(`Finding for unknown rule ${f.ruleId}`);
|
|
28
|
+
g = { rule: summarizeRule(rule, config), findings: [] };
|
|
29
|
+
groups.set(f.ruleId, g);
|
|
30
|
+
}
|
|
31
|
+
g.findings.push(f);
|
|
32
|
+
}
|
|
33
|
+
return [...groups.values()].sort((a, b) => b.rule.severity - a.rule.severity ||
|
|
34
|
+
CATEGORY_ORDER.indexOf(a.rule.category) - CATEGORY_ORDER.indexOf(b.rule.category) ||
|
|
35
|
+
b.findings.length - a.findings.length ||
|
|
36
|
+
a.rule.id.localeCompare(b.rule.id));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=rank.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rank.js","sourceRoot":"","sources":["../../src/engine/rank.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EACL,cAAc,GAMf,MAAM,mBAAmB,CAAC;AAmB3B,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAU,EAAE,MAAsB,EAAY,EAAE,CAChF,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC;AAEhD,MAAM,UAAU,aAAa,CAAC,IAAU,EAAE,MAAsB;IAC9D,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC;QACzC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACnB,GAAG,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,MAAM,EAAE,IAAI,CAAC,MAAM;KACpB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,QAAmB,EAAE,KAAa,EAAE,MAAsB;IAC7E,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC9C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;YACnE,CAAC,GAAG,EAAE,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YACxD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC9B,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ;QACjC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;QACjF,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM;QACrC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACrC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Indexes } from "../index/build.js";
|
|
2
|
+
import type { Model } from "../model/types.js";
|
|
3
|
+
import type { Finding, Rule } from "../rules/types.js";
|
|
4
|
+
import type { ResolvedConfig } from "./config.js";
|
|
5
|
+
export interface SkippedRule {
|
|
6
|
+
id: string;
|
|
7
|
+
reason: "disabled" | "needsLiveModel";
|
|
8
|
+
}
|
|
9
|
+
export interface RuleError {
|
|
10
|
+
id: string;
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export interface RunResult {
|
|
14
|
+
findings: Finding[];
|
|
15
|
+
rulesRun: string[];
|
|
16
|
+
rulesSkipped: SkippedRule[];
|
|
17
|
+
ruleErrors: RuleError[];
|
|
18
|
+
ignored: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function runRules(model: Model, indexes: Indexes, rules: Rule[], config: ResolvedConfig): RunResult;
|
|
21
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/engine/run.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAAC;CACvC;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,QAAQ,CACtB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,IAAI,EAAE,EACb,MAAM,EAAE,cAAc,GACrB,SAAS,CAqCX"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isIgnored } from "./ignore.js";
|
|
2
|
+
export function runRules(model, indexes, rules, config) {
|
|
3
|
+
const result = {
|
|
4
|
+
findings: [],
|
|
5
|
+
rulesRun: [],
|
|
6
|
+
rulesSkipped: [],
|
|
7
|
+
ruleErrors: [],
|
|
8
|
+
ignored: 0,
|
|
9
|
+
};
|
|
10
|
+
for (const rule of rules) {
|
|
11
|
+
if (config.disabled.has(rule.id)) {
|
|
12
|
+
result.rulesSkipped.push({ id: rule.id, reason: "disabled" });
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (rule.status === "needsLiveModel") {
|
|
16
|
+
result.rulesSkipped.push({ id: rule.id, reason: "needsLiveModel" });
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
result.rulesRun.push(rule.id);
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
raw = rule.check(model, { indexes });
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
result.ruleErrors.push({ id: rule.id, message: e instanceof Error ? e.message : String(e) });
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
for (const f of raw) {
|
|
29
|
+
if (isIgnored(f.object, rule.id)) {
|
|
30
|
+
result.ignored++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const out = { ruleId: rule.id, objectType: f.objectType, objectName: f.objectName };
|
|
34
|
+
if (f.location)
|
|
35
|
+
out.location = f.location;
|
|
36
|
+
if (f.detail !== undefined)
|
|
37
|
+
out.detail = f.detail;
|
|
38
|
+
result.findings.push(out);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/engine/run.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAoBxC,MAAM,UAAU,QAAQ,CACtB,KAAY,EACZ,OAAgB,EAChB,KAAa,EACb,MAAsB;IAEtB,MAAM,MAAM,GAAc;QACxB,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,EAAE;QACZ,YAAY,EAAE,EAAE;QAChB,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,CAAC;KACX,CAAC;IACF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;YACrC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;YACpE,SAAS;QACX,CAAC;QACD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,GAAG,CAAC;QACR,IAAI,CAAC;YACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7F,SAAS;QACX,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,IAAI,SAAS,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACjC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,SAAS;YACX,CAAC;YACD,MAAM,GAAG,GAAY,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;YAC7F,IAAI,CAAC,CAAC,QAAQ;gBAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC;YAC1C,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;YAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LintResult } from "../engine/lint.js";
|
|
2
|
+
import { formatJson } from "./json.js";
|
|
3
|
+
import { formatMarkdown } from "./markdown.js";
|
|
4
|
+
import { formatSarif } from "./sarif.js";
|
|
5
|
+
import { formatText, skippedLine, summaryLine, topGroups, type FormatOptions, type RuleHelp } from "./text.js";
|
|
6
|
+
export declare const FORMATS: readonly ["text", "json", "markdown", "sarif"];
|
|
7
|
+
export type FormatName = (typeof FORMATS)[number];
|
|
8
|
+
export declare function formatResult(name: FormatName, result: LintResult, options?: FormatOptions): string;
|
|
9
|
+
export { formatJson, formatMarkdown, formatSarif, formatText, skippedLine, summaryLine, topGroups };
|
|
10
|
+
export type { FormatOptions, RuleHelp };
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,SAAS,EACT,KAAK,aAAa,EAClB,KAAK,QAAQ,EACd,MAAM,WAAW,CAAC;AAEnB,eAAO,MAAM,OAAO,gDAAiD,CAAC;AACtE,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC;AAElD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAaR;AAED,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AACpG,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { formatJson } from "./json.js";
|
|
2
|
+
import { formatMarkdown } from "./markdown.js";
|
|
3
|
+
import { formatSarif } from "./sarif.js";
|
|
4
|
+
import { formatText, skippedLine, summaryLine, topGroups, } from "./text.js";
|
|
5
|
+
export const FORMATS = ["text", "json", "markdown", "sarif"];
|
|
6
|
+
export function formatResult(name, result, options = {}) {
|
|
7
|
+
switch (name) {
|
|
8
|
+
case "text":
|
|
9
|
+
return formatText(result, options);
|
|
10
|
+
case "json":
|
|
11
|
+
return formatJson(result, options);
|
|
12
|
+
case "markdown":
|
|
13
|
+
return formatMarkdown(result, options);
|
|
14
|
+
case "sarif":
|
|
15
|
+
return formatSarif(result, options);
|
|
16
|
+
default:
|
|
17
|
+
throw new Error(`Unknown format: ${String(name)}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export { formatJson, formatMarkdown, formatSarif, formatText, skippedLine, summaryLine, topGroups };
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,UAAU,EACV,WAAW,EACX,WAAW,EACX,SAAS,GAGV,MAAM,WAAW,CAAC;AAEnB,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAGtE,MAAM,UAAU,YAAY,CAC1B,IAAgB,EAChB,MAAkB,EAClB,UAAyB,EAAE;IAE3B,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,KAAK,MAAM;YACT,OAAO,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,KAAK,UAAU;YACb,OAAO,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACzC,KAAK,OAAO;YACV,OAAO,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC;YACE,MAAM,IAAI,KAAK,CAAC,mBAAmB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC;AACH,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/format/json.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAiBlF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { VERSION } from "../version.js";
|
|
2
|
+
export function formatJson(result, options = {}) {
|
|
3
|
+
const doc = {
|
|
4
|
+
version: 1,
|
|
5
|
+
tool: { name: "pbiplint", version: options.toolVersion ?? VERSION },
|
|
6
|
+
summary: result.summary,
|
|
7
|
+
groups: result.groups.map((g) => ({
|
|
8
|
+
rule: g.rule,
|
|
9
|
+
count: g.findings.length,
|
|
10
|
+
findings: g.findings.map((f) => ({
|
|
11
|
+
objectType: f.objectType,
|
|
12
|
+
objectName: f.objectName,
|
|
13
|
+
...(f.location ? { file: f.location.file, line: f.location.line } : {}),
|
|
14
|
+
...(f.detail !== undefined ? { detail: f.detail } : {}),
|
|
15
|
+
})),
|
|
16
|
+
})),
|
|
17
|
+
};
|
|
18
|
+
return JSON.stringify(doc, null, 2) + "\n";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/format/json.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,MAAM,UAAU,UAAU,CAAC,MAAkB,EAAE,UAAyB,EAAE;IACxE,MAAM,GAAG,GAAG;QACV,OAAO,EAAE,CAAC;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,WAAW,IAAI,OAAO,EAAE;QACnE,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAChC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,KAAK,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM;YACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC/B,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACvE,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxD,CAAC,CAAC;SACJ,CAAC,CAAC;KACJ,CAAC;IACF,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/format/markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAmD,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAIhG,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,GAAE,aAAkB,GAAG,MAAM,CA8BvF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { SEVERITY_LABEL } from "../rules/types.js";
|
|
2
|
+
import { locationOf, skippedLine, summaryLine, topGroups } from "./text.js";
|
|
3
|
+
const cell = (s) => s.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
4
|
+
export function formatMarkdown(result, _options = {}) {
|
|
5
|
+
const out = [
|
|
6
|
+
"# pbiplint report",
|
|
7
|
+
"",
|
|
8
|
+
`${summaryLine(result)}. ${skippedLine(result)}.`,
|
|
9
|
+
"",
|
|
10
|
+
];
|
|
11
|
+
if (result.groups.length === 0) {
|
|
12
|
+
out.push("No findings.", "");
|
|
13
|
+
return out.join("\n");
|
|
14
|
+
}
|
|
15
|
+
out.push("## Fix these first", "");
|
|
16
|
+
topGroups(result).forEach((g, i) => out.push(`${i + 1}. **${g.rule.name}** (${g.findings.length}) [${g.rule.id}](${g.rule.url})`));
|
|
17
|
+
out.push("");
|
|
18
|
+
for (const g of result.groups) {
|
|
19
|
+
out.push(`## ${SEVERITY_LABEL[g.rule.severity].toUpperCase()}: ${g.rule.name} (${g.findings.length})`, "");
|
|
20
|
+
out.push(`[${g.rule.id}](${g.rule.url}) · ${g.rule.category}`, "");
|
|
21
|
+
out.push("| Object | Type | Location | Detail |", "|---|---|---|---|");
|
|
22
|
+
for (const f of g.findings)
|
|
23
|
+
out.push(`| \`${cell(f.objectName)}\` | ${f.objectType} | ${locationOf(f)} | ${cell(f.detail ?? "")} |`);
|
|
24
|
+
out.push("");
|
|
25
|
+
}
|
|
26
|
+
return out.join("\n");
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=markdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/format/markdown.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAsB,MAAM,WAAW,CAAC;AAEhG,MAAM,IAAI,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEhF,MAAM,UAAU,cAAc,CAAC,MAAkB,EAAE,WAA0B,EAAE;IAC7E,MAAM,GAAG,GAAa;QACpB,mBAAmB;QACnB,EAAE;QACF,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,WAAW,CAAC,MAAM,CAAC,GAAG;QACjD,EAAE;KACH,CAAC;IACF,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QAC7B,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC,CAAC;IACnC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACjC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAC9F,CAAC;IACF,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACb,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9B,GAAG,CAAC,IAAI,CACN,MAAM,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,EAC5F,EAAE,CACH,CAAC;QACF,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACnE,GAAG,CAAC,IAAI,CAAC,uCAAuC,EAAE,mBAAmB,CAAC,CAAC;QACvE,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ;YACxB,GAAG,CAAC,IAAI,CACN,OAAO,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,UAAU,MAAM,UAAU,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,IAAI,CAC/F,CAAC;QACJ,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sarif.d.ts","sourceRoot":"","sources":["../../src/format/sarif.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,WAAW,CAAC;AAWzD,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAqEnF"}
|