@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
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE: import("../types.js").Rule;
|
|
2
|
+
export declare const HIDE_FOREIGN_KEYS: import("../types.js").Rule;
|
|
3
|
+
export declare const MARK_PRIMARY_KEYS: import("../types.js").Rule;
|
|
4
|
+
export declare const REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES: import("../types.js").Rule;
|
|
5
|
+
export declare const SNOWFLAKE_SCHEMA_ARCHITECTURE: import("../types.js").Rule;
|
|
6
|
+
export declare const ENSURE_TABLES_HAVE_RELATIONSHIPS: import("../types.js").Rule;
|
|
7
|
+
export declare const MANY_TO_MANY_RELATIONSHIPS_SHOULD_BE_SINGLE_DIRECTION: import("../types.js").Rule;
|
|
8
|
+
export declare const CHECK_IF_BIDIRECTIONAL_AND_MANY_TO_MANY_RELATIONSHIPS_ARE_VALID: import("../types.js").Rule;
|
|
9
|
+
export declare const RELATIONSHIP_COLUMNS_SAME_DATA_TYPE: import("../types.js").Rule;
|
|
10
|
+
export declare const INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED: import("../types.js").Rule;
|
|
11
|
+
export declare const AVOID_EXCESSIVE_BIDIRECTIONAL_OR_MANY_TO_MANY_RELATIONSHIPS: import("../types.js").Rule;
|
|
12
|
+
export declare const AVOID_USING_MANY_TO_MANY_RELATIONSHIPS_ON_TABLES_USED_FOR_DYNAMIC_ROW_LEVEL_SECURITY: import("../types.js").Rule;
|
|
13
|
+
export declare const relationshipRules: import("../types.js").Rule[];
|
|
14
|
+
//# sourceMappingURL=relationships.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../../src/rules/microsoft-bpa/relationships.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,mDAAmD,4BAQ/D,CAAC;AAIF,eAAO,MAAM,iBAAiB,4BAY7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,4BAgB7B,CAAC;AAEF,eAAO,MAAM,0CAA0C,4BAiBtD,CAAC;AAEF,eAAO,MAAM,6BAA6B,4BASzC,CAAC;AAEF,eAAO,MAAM,gCAAgC,4BAM5C,CAAC;AAEF,eAAO,MAAM,qDAAqD,4BAIjE,CAAC;AAEF,eAAO,MAAM,+DAA+D,4BAI3E,CAAC;AAEF,eAAO,MAAM,mCAAmC,4BAa/C,CAAC;AAIF,eAAO,MAAM,+CAA+C,4BAkB3D,CAAC;AAGF,eAAO,MAAM,2DAA2D,4BAOvE,CAAC;AAEF,eAAO,MAAM,oFAAoF,4BAc9F,CAAC;AAEJ,eAAO,MAAM,iBAAiB,8BAa7B,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { allCalculationItems, allColumns, allMeasures, allTablePermissions, dataType, escapeRegExp, finding, tablesInScope, } from "../helpers.js";
|
|
2
|
+
import { bpaRule } from "./define.js";
|
|
3
|
+
const isManyToMany = (r) => r.fromCardinality === "many" && r.toCardinality === "many";
|
|
4
|
+
const isBidirectional = (r) => r.crossFilteringBehavior === "bothdirections";
|
|
5
|
+
export const RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE = bpaRule("RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE", (m, { indexes: { relationships } }) => allColumns(m)
|
|
6
|
+
.filter((c) => relationships.forColumn(c.table.name, c.name).length > 0 && dataType(c) !== "int64")
|
|
7
|
+
.map(finding.column));
|
|
8
|
+
// Quirk kept on purpose: the source compares FromColumn.Name to the column's name only, not table plus
|
|
9
|
+
// column, so a dimension key that shares its name with the foreign key is flagged as well.
|
|
10
|
+
export const HIDE_FOREIGN_KEYS = bpaRule("HIDE_FOREIGN_KEYS", (m, { indexes: { relationships } }) => allColumns(m)
|
|
11
|
+
.filter((c) => !c.isHidden &&
|
|
12
|
+
relationships
|
|
13
|
+
.forColumn(c.table.name, c.name)
|
|
14
|
+
.some((r) => r.fromColumn === c.name && r.fromCardinality === "many"))
|
|
15
|
+
.map(finding.column));
|
|
16
|
+
export const MARK_PRIMARY_KEYS = bpaRule("MARK_PRIMARY_KEYS", (m, { indexes: { relationships } }) => allColumns(m)
|
|
17
|
+
.filter((c) => !c.isKey &&
|
|
18
|
+
c.table.dataCategory !== "Time" &&
|
|
19
|
+
relationships
|
|
20
|
+
.forColumn(c.table.name, c.name)
|
|
21
|
+
.some((r) => r.toTable === c.table.name && r.toColumn === c.name && r.toCardinality === "one"))
|
|
22
|
+
.map(finding.column));
|
|
23
|
+
export const REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES = bpaRule("REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES", (m, { indexes: { relationships } }) => {
|
|
24
|
+
const all = allColumns(m);
|
|
25
|
+
return all
|
|
26
|
+
.filter((c) => relationships.forColumn(c.table.name, c.name).length === 0 &&
|
|
27
|
+
all.some((o) => o.name === c.name &&
|
|
28
|
+
o.table !== c.table &&
|
|
29
|
+
relationships.forTable(o.table.name).some((r) => r.fromTable === c.table.name)))
|
|
30
|
+
.map(finding.column);
|
|
31
|
+
});
|
|
32
|
+
export const SNOWFLAKE_SCHEMA_ARCHITECTURE = bpaRule("SNOWFLAKE_SCHEMA_ARCHITECTURE", (m, { indexes: { relationships } }) => tablesInScope(m)
|
|
33
|
+
.filter((t) => {
|
|
34
|
+
const rels = relationships.forTable(t.name);
|
|
35
|
+
return rels.some((r) => r.fromTable === t.name) && rels.some((r) => r.toTable === t.name);
|
|
36
|
+
})
|
|
37
|
+
.map(finding.table));
|
|
38
|
+
export const ENSURE_TABLES_HAVE_RELATIONSHIPS = bpaRule("ENSURE_TABLES_HAVE_RELATIONSHIPS", (m, { indexes: { relationships } }) => tablesInScope(m)
|
|
39
|
+
.filter((t) => relationships.forTable(t.name).length === 0)
|
|
40
|
+
.map(finding.table));
|
|
41
|
+
export const MANY_TO_MANY_RELATIONSHIPS_SHOULD_BE_SINGLE_DIRECTION = bpaRule("MANY-TO-MANY_RELATIONSHIPS_SHOULD_BE_SINGLE-DIRECTION", (m) => m.relationships.filter((r) => isManyToMany(r) && isBidirectional(r)).map(finding.relationship));
|
|
42
|
+
export const CHECK_IF_BIDIRECTIONAL_AND_MANY_TO_MANY_RELATIONSHIPS_ARE_VALID = bpaRule("CHECK_IF_BI-DIRECTIONAL_AND_MANY-TO-MANY_RELATIONSHIPS_ARE_VALID", (m) => m.relationships.filter((r) => isManyToMany(r) || isBidirectional(r)).map(finding.relationship));
|
|
43
|
+
export const RELATIONSHIP_COLUMNS_SAME_DATA_TYPE = bpaRule("RELATIONSHIP_COLUMNS_SAME_DATA_TYPE", (m) => {
|
|
44
|
+
const column = (table, name) => m.tables.find((t) => t.name === table)?.columns.find((c) => c.name === name);
|
|
45
|
+
return m.relationships
|
|
46
|
+
.filter((r) => {
|
|
47
|
+
const from = column(r.fromTable, r.fromColumn);
|
|
48
|
+
const to = column(r.toTable, r.toColumn);
|
|
49
|
+
return from !== undefined && to !== undefined && dataType(from) !== dataType(to);
|
|
50
|
+
})
|
|
51
|
+
.map(finding.relationship);
|
|
52
|
+
});
|
|
53
|
+
// The source builds its regex from raw names; names are escaped here so a table called "Date (Order)"
|
|
54
|
+
// cannot break the pattern. Argument order matters: USERELATIONSHIP(to, from) does not count, as in the source.
|
|
55
|
+
export const INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED = bpaRule("INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED", (m) => {
|
|
56
|
+
const expressions = [
|
|
57
|
+
...allMeasures(m).map((x) => x.expression),
|
|
58
|
+
...allCalculationItems(m).map((i) => i.expression),
|
|
59
|
+
];
|
|
60
|
+
return m.relationships
|
|
61
|
+
.filter((r) => {
|
|
62
|
+
if (r.isActive)
|
|
63
|
+
return false;
|
|
64
|
+
const re = new RegExp(`USERELATIONSHIP\\s*\\(\\s*'*${escapeRegExp(r.fromTable)}'*\\[${escapeRegExp(r.fromColumn)}\\]\\s*,\\s*'*${escapeRegExp(r.toTable)}'*\\[${escapeRegExp(r.toColumn)}\\]`, "i");
|
|
65
|
+
return !expressions.some((e) => re.test(e));
|
|
66
|
+
})
|
|
67
|
+
.map(finding.relationship);
|
|
68
|
+
});
|
|
69
|
+
// A relationship that is both bi-directional and many-to-many counts twice, as in the source.
|
|
70
|
+
export const AVOID_EXCESSIVE_BIDIRECTIONAL_OR_MANY_TO_MANY_RELATIONSHIPS = bpaRule("AVOID_EXCESSIVE_BI-DIRECTIONAL_OR_MANY-TO-MANY_RELATIONSHIPS", (m) => {
|
|
71
|
+
const rels = m.relationships;
|
|
72
|
+
const count = rels.filter(isBidirectional).length + rels.filter(isManyToMany).length;
|
|
73
|
+
return count / Math.max(rels.length, 1) > 0.3 ? [finding.model(m)] : [];
|
|
74
|
+
});
|
|
75
|
+
export const AVOID_USING_MANY_TO_MANY_RELATIONSHIPS_ON_TABLES_USED_FOR_DYNAMIC_ROW_LEVEL_SECURITY = bpaRule("AVOID_USING_MANY-TO-MANY_RELATIONSHIPS_ON_TABLES_USED_FOR_DYNAMIC_ROW_LEVEL_SECURITY", (m, { indexes: { relationships } }) => {
|
|
76
|
+
const permissions = allTablePermissions(m);
|
|
77
|
+
return m.tables
|
|
78
|
+
.filter((t) => t.kind === "table" &&
|
|
79
|
+
relationships.forTable(t.name).some(isManyToMany) &&
|
|
80
|
+
permissions.some((tp) => tp.table === t.name && (tp.filter ?? "").length > 0))
|
|
81
|
+
.map(finding.table);
|
|
82
|
+
});
|
|
83
|
+
export const relationshipRules = [
|
|
84
|
+
RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE,
|
|
85
|
+
HIDE_FOREIGN_KEYS,
|
|
86
|
+
MARK_PRIMARY_KEYS,
|
|
87
|
+
REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES,
|
|
88
|
+
SNOWFLAKE_SCHEMA_ARCHITECTURE,
|
|
89
|
+
ENSURE_TABLES_HAVE_RELATIONSHIPS,
|
|
90
|
+
MANY_TO_MANY_RELATIONSHIPS_SHOULD_BE_SINGLE_DIRECTION,
|
|
91
|
+
CHECK_IF_BIDIRECTIONAL_AND_MANY_TO_MANY_RELATIONSHIPS_ARE_VALID,
|
|
92
|
+
RELATIONSHIP_COLUMNS_SAME_DATA_TYPE,
|
|
93
|
+
INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED,
|
|
94
|
+
AVOID_EXCESSIVE_BIDIRECTIONAL_OR_MANY_TO_MANY_RELATIONSHIPS,
|
|
95
|
+
AVOID_USING_MANY_TO_MANY_RELATIONSHIPS_ON_TABLES_USED_FOR_DYNAMIC_ROW_LEVEL_SECURITY,
|
|
96
|
+
];
|
|
97
|
+
//# sourceMappingURL=relationships.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relationships.js","sourceRoot":"","sources":["../../../src/rules/microsoft-bpa/relationships.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const MODEL_SHOULD_HAVE_A_DATE_TABLE: import("../types.js").Rule;
|
|
2
|
+
export declare const DATE_CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE: import("../types.js").Rule;
|
|
3
|
+
export declare const REMOVE_AUTO_DATE_TABLE: import("../types.js").Rule;
|
|
4
|
+
export declare const REDUCE_USAGE_OF_CALCULATED_TABLES: import("../types.js").Rule;
|
|
5
|
+
export declare const REDUCE_NUMBER_OF_CALCULATED_COLUMNS: import("../types.js").Rule;
|
|
6
|
+
export declare const UNPIVOT_PIVOTED_MONTH_DATA: import("../types.js").Rule;
|
|
7
|
+
export declare const PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES: import("../types.js").Rule;
|
|
8
|
+
export declare const MINIMIZE_POWER_QUERY_TRANSFORMATIONS: import("../types.js").Rule;
|
|
9
|
+
export declare const MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS: import("../types.js").Rule;
|
|
10
|
+
export declare const MEASURES_USING_TIME_INTELLIGENCE_AND_MODEL_IS_USING_DIRECT_QUERY: import("../types.js").Rule;
|
|
11
|
+
export declare const LIMIT_ROW_LEVEL_SECURITY_LOGIC: import("../types.js").Rule;
|
|
12
|
+
export declare const CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_IS_NECESSARY: import("../types.js").Rule;
|
|
13
|
+
export declare const AVOID_THE_USERELATIONSHIP_FUNCTION_AND_RLS_AGAINST_THE_SAME_TABLE: import("../types.js").Rule;
|
|
14
|
+
export declare const OBJECTS_WITH_NO_DESCRIPTION: import("../types.js").Rule;
|
|
15
|
+
export declare const tableRules: import("../types.js").Rule[];
|
|
16
|
+
//# sourceMappingURL=tables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../../src/rules/microsoft-bpa/tables.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,8BAA8B,4BAE1C,CAAC;AAEF,eAAO,MAAM,qDAAqD,4BAYjE,CAAC;AAEF,eAAO,MAAM,sBAAsB,4BAQlC,CAAC;AAEF,eAAO,MAAM,iCAAiC,4BAE7C,CAAC;AAEF,eAAO,MAAM,mCAAmC,4BAI/C,CAAC;AAIF,eAAO,MAAM,0BAA0B,4BAQtC,CAAC;AAGF,eAAO,MAAM,kEAAkE,4BAQ9E,CAAC;AAmBF,eAAO,MAAM,oCAAoC,4BAShD,CAAC;AAEF,eAAO,MAAM,4CAA4C,4BAQxD,CAAC;AA4CF,eAAO,MAAM,gEAAgE,4BAQ5E,CAAC;AAKF,eAAO,MAAM,8BAA8B,4BAe1C,CAAC;AAGF,eAAO,MAAM,gDAAgD,4BAU5D,CAAC;AAEF,eAAO,MAAM,iEAAiE,4BAgB7E,CAAC;AAKF,eAAO,MAAM,2BAA2B,4BAMvC,CAAC;AAEF,eAAO,MAAM,UAAU,8BAetB,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { allColumns, allMeasures, allPartitions, allTablePermissions, dataType, escapeRegExp, expressionObjects, finding, isBlank, isDirectQueryTable, isNumericType, tablesInScope, } from "../helpers.js";
|
|
2
|
+
import { bpaRule } from "./define.js";
|
|
3
|
+
const hasDateTimeKey = (t) => t.columns.some((c) => c.isKey && dataType(c) === "datetime");
|
|
4
|
+
export const MODEL_SHOULD_HAVE_A_DATE_TABLE = bpaRule("MODEL_SHOULD_HAVE_A_DATE_TABLE", (m) => m.tables.some((t) => t.dataCategory === "Time" && hasDateTimeKey(t)) ? [] : [finding.model(m)]);
|
|
5
|
+
export const DATE_CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE = bpaRule("DATE/CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE", (m) => tablesInScope(m)
|
|
6
|
+
.filter((t) => {
|
|
7
|
+
const u = t.name.toUpperCase();
|
|
8
|
+
return ((u.includes("DATE") || u.includes("CALENDAR")) &&
|
|
9
|
+
(t.dataCategory !== "Time" || !hasDateTimeKey(t)));
|
|
10
|
+
})
|
|
11
|
+
.map(finding.table));
|
|
12
|
+
export const REMOVE_AUTO_DATE_TABLE = bpaRule("REMOVE_AUTO-DATE_TABLE", (m) => m.tables
|
|
13
|
+
.filter((t) => t.kind === "calculated" &&
|
|
14
|
+
(t.name.startsWith("DateTableTemplate_") || t.name.startsWith("LocalDateTable_")))
|
|
15
|
+
.map(finding.table));
|
|
16
|
+
export const REDUCE_USAGE_OF_CALCULATED_TABLES = bpaRule("REDUCE_USAGE_OF_CALCULATED_TABLES", (m) => m.tables.filter((t) => t.kind === "calculated").map(finding.table));
|
|
17
|
+
export const REDUCE_NUMBER_OF_CALCULATED_COLUMNS = bpaRule("REDUCE_NUMBER_OF_CALCULATED_COLUMNS", (m) => allColumns(m).filter((c) => c.kind === "calculated").length > 5 ? [finding.model(m)] : []);
|
|
18
|
+
const MONTHS = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN"];
|
|
19
|
+
export const UNPIVOT_PIVOTED_MONTH_DATA = bpaRule("UNPIVOT_PIVOTED_(MONTH)_DATA", (m) => tablesInScope(m)
|
|
20
|
+
.filter((t) => MONTHS.every((mo) => t.columns.some((c) => c.name.toUpperCase().includes(mo) && isNumericType(c))))
|
|
21
|
+
.map(finding.table));
|
|
22
|
+
// Scope is Table only: calculated tables and calculation groups are not checked.
|
|
23
|
+
export const PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES = bpaRule("PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES", (m) => m.tables
|
|
24
|
+
.filter((t) => t.kind === "table" && t.partitions.length === 1 && t.partitions[0].name !== t.name)
|
|
25
|
+
.map(finding.table));
|
|
26
|
+
const POWER_QUERY_PATTERNS = [
|
|
27
|
+
"Table.Combine(",
|
|
28
|
+
"Table.Join(",
|
|
29
|
+
"Table.NestedJoin(",
|
|
30
|
+
"Table.AddColumn(",
|
|
31
|
+
"Table.Group(",
|
|
32
|
+
"Table.Sort(",
|
|
33
|
+
"Table.Pivot(",
|
|
34
|
+
"Table.Unpivot(",
|
|
35
|
+
"Table.UnpivotOtherColumns(",
|
|
36
|
+
"Table.Distinct(",
|
|
37
|
+
'[Query="SELECT',
|
|
38
|
+
"Value.NativeQuery",
|
|
39
|
+
"OleDb.Query",
|
|
40
|
+
"Odbc.Query",
|
|
41
|
+
];
|
|
42
|
+
export const MINIMIZE_POWER_QUERY_TRANSFORMATIONS = bpaRule("MINIMIZE_POWER_QUERY_TRANSFORMATIONS", (m) => allPartitions(m)
|
|
43
|
+
.filter((p) => p.sourceType === "m" && POWER_QUERY_PATTERNS.some((s) => (p.source ?? "").includes(s)))
|
|
44
|
+
.map(finding.partition));
|
|
45
|
+
export const MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS = bpaRule("MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS", (m) => m.tables.some(isDirectQueryTable) &&
|
|
46
|
+
!allColumns(m).some((c) => c.hasAlternateOf) &&
|
|
47
|
+
String(m.props.defaultpowerbidatasourceversion ?? "").toLowerCase() === "powerbi_v3"
|
|
48
|
+
? [finding.model(m)]
|
|
49
|
+
: []);
|
|
50
|
+
const TIME_INTELLIGENCE_FUNCTIONS = [
|
|
51
|
+
"CLOSINGBALANCEMONTH",
|
|
52
|
+
"CLOSINGBALANCEQUARTER",
|
|
53
|
+
"CLOSINGBALANCEYEAR",
|
|
54
|
+
"DATEADD",
|
|
55
|
+
"DATESBETWEEN",
|
|
56
|
+
"DATESINPERIOD",
|
|
57
|
+
"DATESMTD",
|
|
58
|
+
"DATESQTD",
|
|
59
|
+
"DATESYTD",
|
|
60
|
+
"ENDOFMONTH",
|
|
61
|
+
"ENDOFQUARTER",
|
|
62
|
+
"ENDOFYEAR",
|
|
63
|
+
"FIRSTDATE",
|
|
64
|
+
"FIRSTNONBLANK",
|
|
65
|
+
"FIRSTNONBLANKVALUE",
|
|
66
|
+
"LASTDATE",
|
|
67
|
+
"LASTNONBLANK",
|
|
68
|
+
"LASTNONBLANKVALUE",
|
|
69
|
+
"NEXTDAY",
|
|
70
|
+
"NEXTMONTH",
|
|
71
|
+
"NEXTQUARTER",
|
|
72
|
+
"NEXTYEAR",
|
|
73
|
+
"OPENINGBALANCEMONTH",
|
|
74
|
+
"OPENINGBALANCEQUARTER",
|
|
75
|
+
"OPENINGBALANCEYEAR",
|
|
76
|
+
"PARALLELPERIOD",
|
|
77
|
+
"PREVIOUSDAY",
|
|
78
|
+
"PREVIOUSMONTH",
|
|
79
|
+
"PREVIOUSQUARTER",
|
|
80
|
+
"PREVIOUSYEAR",
|
|
81
|
+
"SAMEPERIODLASTYEAR",
|
|
82
|
+
"STARTOFMONTH",
|
|
83
|
+
"STARTOFQUARTER",
|
|
84
|
+
"STARTOFYEAR",
|
|
85
|
+
"TOTALMTD",
|
|
86
|
+
"TOTALQTD",
|
|
87
|
+
"TOTALYTD",
|
|
88
|
+
];
|
|
89
|
+
// The source patterns have no (?i), so this one is case-sensitive.
|
|
90
|
+
const TIME_INTELLIGENCE = new RegExp(`(?:${TIME_INTELLIGENCE_FUNCTIONS.join("|")})\\s*\\(`);
|
|
91
|
+
export const MEASURES_USING_TIME_INTELLIGENCE_AND_MODEL_IS_USING_DIRECT_QUERY = bpaRule("MEASURES_USING_TIME_INTELLIGENCE_AND_MODEL_IS_USING_DIRECT_QUERY", (m) => m.tables.some(isDirectQueryTable)
|
|
92
|
+
? expressionObjects(m, ["measure", "calculationItem"])
|
|
93
|
+
.filter((o) => TIME_INTELLIGENCE.test(o.expression))
|
|
94
|
+
.map((o) => o.finding)
|
|
95
|
+
: []);
|
|
96
|
+
const RLS_FUNCTIONS = [/RIGHT\s*\(/i, /LEFT\s*\(/i, /UPPER\s*\(/i, /LOWER\s*\(/i, /FIND\s*\(/i];
|
|
97
|
+
// The source removes spaces from the filter before matching, so "L E F T(" matches and so does "BRIGHT(".
|
|
98
|
+
export const LIMIT_ROW_LEVEL_SECURITY_LOGIC = bpaRule("LIMIT_ROW_LEVEL_SECURITY_(RLS)_LOGIC", (m) => {
|
|
99
|
+
const permissions = allTablePermissions(m);
|
|
100
|
+
return tablesInScope(m)
|
|
101
|
+
.filter((t) => permissions.some((tp) => tp.table === t.name &&
|
|
102
|
+
tp.filter !== undefined &&
|
|
103
|
+
RLS_FUNCTIONS.some((re) => re.test(tp.filter.replace(/ /g, "")))))
|
|
104
|
+
.map(finding.table);
|
|
105
|
+
});
|
|
106
|
+
// No \s* between the function name and the parenthesis in the source, so "USERPRINCIPALNAME ()" is not matched.
|
|
107
|
+
export const CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_IS_NECESSARY = bpaRule("CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_(RLS)_IS_NECESSARY", (m) => allTablePermissions(m)
|
|
108
|
+
.filter((tp) => tp.filter !== undefined &&
|
|
109
|
+
(/USERNAME\(/i.test(tp.filter) || /USERPRINCIPALNAME\(/i.test(tp.filter)))
|
|
110
|
+
.map(finding.tablePermission));
|
|
111
|
+
export const AVOID_THE_USERELATIONSHIP_FUNCTION_AND_RLS_AGAINST_THE_SAME_TABLE = bpaRule("AVOID_THE_USERELATIONSHIP_FUNCTION_AND_RLS_AGAINST_THE_SAME_TABLE", (m) => {
|
|
112
|
+
const permissions = allTablePermissions(m);
|
|
113
|
+
const measures = allMeasures(m);
|
|
114
|
+
return tablesInScope(m)
|
|
115
|
+
.filter((t) => {
|
|
116
|
+
if (!permissions.some((tp) => tp.table === t.name && tp.filter !== undefined))
|
|
117
|
+
return false;
|
|
118
|
+
const re = new RegExp(`USERELATIONSHIP\\s*\\(\\s*.+?(?=\\])\\]\\s*,\\s*'*${escapeRegExp(t.name)}'*\\[`, "i");
|
|
119
|
+
return measures.some((x) => re.test(x.expression));
|
|
120
|
+
})
|
|
121
|
+
.map(finding.table);
|
|
122
|
+
});
|
|
123
|
+
// Scope: Table, Measure, DataColumn, CalculatedColumn, CalculatedTable, CalculatedTableColumn, CalculationGroup.
|
|
124
|
+
// Visibility is the object's own IsHidden (a visible column in a hidden table is still reported).
|
|
125
|
+
// Findings follow model order: each table, then its columns, then its measures.
|
|
126
|
+
export const OBJECTS_WITH_NO_DESCRIPTION = bpaRule("OBJECTS_WITH_NO_DESCRIPTION", (m) => m.tables.flatMap((t) => [
|
|
127
|
+
...(isBlank(t.description) && !t.isHidden ? [finding.table(t)] : []),
|
|
128
|
+
...t.columns.filter((c) => isBlank(c.description) && !c.isHidden).map(finding.column),
|
|
129
|
+
...t.measures.filter((x) => isBlank(x.description) && !x.isHidden).map(finding.measure),
|
|
130
|
+
]));
|
|
131
|
+
export const tableRules = [
|
|
132
|
+
MODEL_SHOULD_HAVE_A_DATE_TABLE,
|
|
133
|
+
DATE_CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE,
|
|
134
|
+
REMOVE_AUTO_DATE_TABLE,
|
|
135
|
+
REDUCE_USAGE_OF_CALCULATED_TABLES,
|
|
136
|
+
REDUCE_NUMBER_OF_CALCULATED_COLUMNS,
|
|
137
|
+
UNPIVOT_PIVOTED_MONTH_DATA,
|
|
138
|
+
PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES,
|
|
139
|
+
MINIMIZE_POWER_QUERY_TRANSFORMATIONS,
|
|
140
|
+
MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS,
|
|
141
|
+
MEASURES_USING_TIME_INTELLIGENCE_AND_MODEL_IS_USING_DIRECT_QUERY,
|
|
142
|
+
LIMIT_ROW_LEVEL_SECURITY_LOGIC,
|
|
143
|
+
CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_IS_NECESSARY,
|
|
144
|
+
AVOID_THE_USERELATIONSHIP_FUNCTION_AND_RLS_AGAINST_THE_SAME_TABLE,
|
|
145
|
+
OBJECTS_WITH_NO_DESCRIPTION,
|
|
146
|
+
];
|
|
147
|
+
//# sourceMappingURL=tables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tables.js","sourceRoot":"","sources":["../../../src/rules/microsoft-bpa/tables.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-issue.d.ts","sourceRoot":"","sources":["../../src/rules/parse-issue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,sGAAsG;AACtG,eAAO,MAAM,WAAW,EAAE,IAkBzB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RULE_SUMMARIES } from "./rule-summaries.data.js";
|
|
2
|
+
/** Built-in rule that surfaces parser issues in the same list as everything else (spec section 5). */
|
|
3
|
+
export const PARSE_ISSUE = {
|
|
4
|
+
id: "PARSE_ISSUE",
|
|
5
|
+
name: "TMDL could not be fully parsed",
|
|
6
|
+
category: "Error Prevention",
|
|
7
|
+
severity: 3,
|
|
8
|
+
scope: ["File"],
|
|
9
|
+
description: RULE_SUMMARIES["PARSE_ISSUE"] ?? "TMDL could not be fully parsed",
|
|
10
|
+
references: ["https://learn.microsoft.com/analysis-services/tmdl/tmdl-overview"],
|
|
11
|
+
status: "builtin",
|
|
12
|
+
check: (model) => model.files.flatMap((f) => f.issues.map((issue) => ({
|
|
13
|
+
objectType: "File",
|
|
14
|
+
objectName: issue.file,
|
|
15
|
+
location: { file: issue.file, line: issue.line },
|
|
16
|
+
detail: `${issue.reason}: ${issue.text.trim()}`,
|
|
17
|
+
}))),
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=parse-issue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-issue.js","sourceRoot":"","sources":["../../src/rules/parse-issue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,sGAAsG;AACtG,MAAM,CAAC,MAAM,WAAW,GAAS;IAC/B,EAAE,EAAE,aAAa;IACjB,IAAI,EAAE,gCAAgC;IACtC,QAAQ,EAAE,kBAAkB;IAC5B,QAAQ,EAAE,CAAC;IACX,KAAK,EAAE,CAAC,MAAM,CAAC;IACf,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,IAAI,gCAAgC;IAC9E,UAAU,EAAE,CAAC,kEAAkE,CAAC;IAChF,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CACf,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACxB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACvB,UAAU,EAAE,MAAe;QAC3B,UAAU,EAAE,KAAK,CAAC,IAAI;QACtB,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;QAChD,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;KAChD,CAAC,CAAC,CACJ;CACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-summaries.data.d.ts","sourceRoot":"","sources":["../../src/rules/rule-summaries.data.ts"],"names":[],"mappings":"AAGA,sFAAsF;AACtF,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2I3D,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Generated by scripts/sync-rule-pages.mjs from the first paragraph of each rule page's
|
|
2
|
+
// "What it checks" section. Do not edit by hand: edit the page under rules/ and rerun the script.
|
|
3
|
+
/** What each rule checks, one paragraph in pbiplint's own words, keyed by rule id. */
|
|
4
|
+
export const RULE_SUMMARIES = {
|
|
5
|
+
ADD_DATA_CATEGORY_FOR_COLUMNS: "Columns with no data category whose name contains country, continent, or city and whose type is text, or whose name is exactly latitude or longitude and whose type is decimal or double.",
|
|
6
|
+
"AVOID_BI-DIRECTIONAL_RELATIONSHIPS_AGAINST_HIGH-CARDINALITY_COLUMNS": "Columns in a bi-directional relationship that have more than 100,000 distinct values. Cardinality is not stored in the model files, so pbiplint lists this rule but cannot run it.",
|
|
7
|
+
AVOID_DUPLICATE_MEASURES: "Two or more measures whose DAX is identical once spaces, tabs, and line breaks are removed. Every copy is reported.",
|
|
8
|
+
"AVOID_EXCESSIVE_BI-DIRECTIONAL_OR_MANY-TO-MANY_RELATIONSHIPS": "Models where bi-directional relationships plus many-to-many relationships make up more than 30 percent of all relationships. The finding is on the model, not on any one relationship.",
|
|
9
|
+
AVOID_FLOATING_POINT_DATA_TYPES: "Columns of any kind whose data type is Double, which Power BI Desktop calls Decimal Number.",
|
|
10
|
+
AVOID_INVALID_DESCRIPTION_CHARACTERS: "Descriptions containing a control character other than whitespace. Tabs and line breaks are allowed.",
|
|
11
|
+
AVOID_INVALID_NAME_CHARACTERS: "Object names containing a control character other than whitespace. Tabs and line breaks are allowed here and are covered by `SPECIAL_CHARS_IN_OBJECT_NAMES`.",
|
|
12
|
+
AVOID_STRUCTURED_DATA_SOURCES_WITH_PROVIDER_PARTITIONS: "Partitions whose source is a legacy query, a provider partition, that points at a structured data source.",
|
|
13
|
+
AVOID_THE_USERELATIONSHIP_FUNCTION_AND_RLS_AGAINST_THE_SAME_TABLE: "Tables that have a row-level security filter in any role and are named as the second argument of USERELATIONSHIP in a measure.",
|
|
14
|
+
"AVOID_USING_'1-(X/Y)'_SYNTAX": "Expressions with a number, then plus or minus, then either `SUM('Table'[Column])` followed by a division operator, or a call to DIVIDE. The common shape is `1 - SUM(Sales[Cost]) / SUM(Sales[Amount])`.",
|
|
15
|
+
"AVOID_USING_MANY-TO-MANY_RELATIONSHIPS_ON_TABLES_USED_FOR_DYNAMIC_ROW_LEVEL_SECURITY": "Regular tables that carry a row-level security filter in any role and take part in a many-to-many relationship.",
|
|
16
|
+
AVOID_USING_THE_IFERROR_FUNCTION: "Measures and calculated columns that call IFERROR.",
|
|
17
|
+
CALCULATION_GROUPS_WITH_NO_CALCULATION_ITEMS: "Calculation groups that contain no calculation items.",
|
|
18
|
+
"CHECK_IF_BI-DIRECTIONAL_AND_MANY-TO-MANY_RELATIONSHIPS_ARE_VALID": "Every relationship that is bi-directional, many-to-many, or both. This is a review list at info severity, not a defect.",
|
|
19
|
+
"CHECK_IF_DYNAMIC_ROW_LEVEL_SECURITY_(RLS)_IS_NECESSARY": "Row-level security filters that call USERNAME or USERPRINCIPALNAME. Reported per table permission, at info severity.",
|
|
20
|
+
DATA_COLUMNS_MUST_HAVE_A_SOURCE_COLUMN: "Data columns with no source column. Calculated columns are not checked.",
|
|
21
|
+
"DATE/CALENDAR_TABLES_SHOULD_BE_MARKED_AS_A_DATE_TABLE": "Tables with date or calendar in the name that are not marked as a date table, meaning the data category is not Time or no DateTime column is marked as the key.",
|
|
22
|
+
DATECOLUMN_FORMATSTRING: "DateTime columns with date in the name whose format string is not exactly `mm/dd/yyyy`.",
|
|
23
|
+
DAX_COLUMNS_FULLY_QUALIFIED: "Measures and row-level security filters that refer to a column by its bare name, `[Column]`, instead of `'Table'[Column]`.",
|
|
24
|
+
DAX_MEASURES_UNQUALIFIED: "Measures, calculated columns, calculated tables, and calculation items that refer to a measure with a table prefix, `'Table'[Measure]`.",
|
|
25
|
+
ENSURE_TABLES_HAVE_RELATIONSHIPS: "Tables with no relationship to any other table. Calculation groups are not checked.",
|
|
26
|
+
EVALUATEANDLOG_SHOULD_NOT_BE_USED_IN_PRODUCTION_MODELS: "Measures that call EVALUATEANDLOG.",
|
|
27
|
+
EXPRESSION_RELIANT_OBJECTS_MUST_HAVE_AN_EXPRESSION: "Measures, calculated columns, and calculation items whose expression is empty.",
|
|
28
|
+
FILTER_COLUMN_VALUES: "CALCULATE or CALCULATETABLE whose first filter argument is `FILTER('Table', 'Table'[Column] ...)`.",
|
|
29
|
+
FILTER_MEASURE_VALUES_BY_COLUMNS: "CALCULATE or CALCULATETABLE whose first filter argument is `FILTER('Table', [Measure] ...)`.",
|
|
30
|
+
FIRST_LETTER_OF_OBJECTS_MUST_BE_CAPITALIZED: "Tables, measures, hierarchies, calculated columns, calculated tables, and calculation groups whose first character has an upper case form and is not upper case.",
|
|
31
|
+
FIX_REFERENTIAL_INTEGRITY_VIOLATIONS: "Relationships where the many side holds key values that do not exist on the one side. Row data is not in the model files, so pbiplint lists this rule but cannot run it.",
|
|
32
|
+
"FORMAT_FLAG_COLUMNS_AS_YES/NO_VALUE_STRINGS": "Visible columns whose name starts with Is and whose type is whole number, and visible columns whose name ends with Flag and whose type is not text.",
|
|
33
|
+
HIDE_FACT_TABLE_COLUMNS: "Visible numeric columns that a measure aggregates directly with a fully qualified reference, such as `SUM('Sales'[Amount])`. COUNT, SUM, AVERAGE, MIN, MAX, DISTINCTCOUNT, VALUES, DISTINCT, and their A-suffixed variants count as aggregations.",
|
|
34
|
+
HIDE_FOREIGN_KEYS: "Visible columns whose name matches the from column of a relationship whose from side is many. Only the from cardinality is tested, so a many-to-many relationship counts here too, not just many-to-one.",
|
|
35
|
+
INACTIVE_RELATIONSHIPS_THAT_ARE_NEVER_ACTIVATED: "Inactive relationships that no measure or calculation item activates with USERELATIONSHIP.",
|
|
36
|
+
INTEGER_FORMATTING: 'Measures whose static format string is not a recognized whole-number, currency, or percentage format. The only format strings the rule accepts are `#,0`, `#,0.0`, and any string containing `$` or `%`. A measure with no format string at all fires too, and that is the common case: the rule reads only the format string, so it cannot tell an unformatted currency or ratio from an unformatted count. Each finding says what the rule saw: `no format string`, `format string "0.00"`, or `dynamic format string only`.',
|
|
37
|
+
ISAVAILABLEINMDX_FALSE_NONATTRIBUTE_COLUMNS: "Hidden columns, or columns in hidden tables, that still have IsAvailableInMdx set to true and are not used to sort another column, in a hierarchy, or in a variation, and do not themselves sort by another column.",
|
|
38
|
+
LARGE_TABLES_SHOULD_BE_PARTITIONED: "Tables with more than 25 million rows and a single partition. Row counts are not in the model files, so pbiplint lists this rule but cannot run it.",
|
|
39
|
+
"LIMIT_ROW_LEVEL_SECURITY_(RLS)_LOGIC": "Tables whose row-level security filter, in any role, calls RIGHT, LEFT, UPPER, LOWER, or FIND.",
|
|
40
|
+
"MANY-TO-MANY_RELATIONSHIPS_SHOULD_BE_SINGLE-DIRECTION": "Many-to-many relationships with bi-directional cross filtering.",
|
|
41
|
+
MARK_PRIMARY_KEYS: "Columns on the one side of a relationship, outside date tables, that are not marked as the table's key.",
|
|
42
|
+
MEASURES_SHOULD_NOT_BE_DIRECT_REFERENCES_OF_OTHER_MEASURES: "Measures whose whole expression is a reference to another measure, such as `[Total Sales]`.",
|
|
43
|
+
MEASURES_USING_TIME_INTELLIGENCE_AND_MODEL_IS_USING_DIRECT_QUERY: "Measures and calculation items that call a time intelligence function, in a model where at least one table is in DirectQuery mode.",
|
|
44
|
+
MINIMIZE_POWER_QUERY_TRANSFORMATIONS: "Power Query partitions whose M text contains Table.Combine, Table.Join, Table.NestedJoin, Table.AddColumn, Table.Group, Table.Sort, Table.Pivot, Table.Unpivot, Table.UnpivotOtherColumns, Table.Distinct, a native SQL query, or an OLE DB or ODBC query.",
|
|
45
|
+
MODEL_SHOULD_HAVE_A_DATE_TABLE: "Models with no table that has the data category Time and a DateTime column marked as the key, which is what Mark as date table sets.",
|
|
46
|
+
MODEL_USING_DIRECT_QUERY_AND_NO_AGGREGATIONS: "Models that have at least one DirectQuery table, no aggregation table (no column has an alternateOf mapping), and the PowerBI_V3 data source version, which is every project Desktop writes today.",
|
|
47
|
+
"MONTH_(AS_A_STRING)_MUST_BE_SORTED": "Text columns with month in the name, but not months, that have no sort-by column.",
|
|
48
|
+
MONTHCOLUMN_FORMATSTRING: "DateTime columns with month in the name whose format string is not exactly `MMMM yyyy`.",
|
|
49
|
+
NUMERIC_COLUMN_SUMMARIZE_BY: "Visible whole number, decimal, or double columns whose default summarization is anything other than None.",
|
|
50
|
+
OBJECTS_SHOULD_NOT_START_OR_END_WITH_A_SPACE: "Names that start or end with a space, for the model, tables, measures, hierarchies, perspectives, partitions, data columns, and calculated columns.",
|
|
51
|
+
OBJECTS_WITH_NO_DESCRIPTION: "Visible tables, columns, measures, and calculation groups with no description. Visibility is the object's own flag.",
|
|
52
|
+
PARSE_ISSUE: "Lines the TMDL parser could not use: space indentation, an unterminated code fence, a line at an impossible indentation, a line in no form the parser recognizes, and a `///` description with a blank line between it and its declaration.",
|
|
53
|
+
PARTITION_NAME_SHOULD_MATCH_TABLE_NAME_FOR_SINGLE_PARTITION_TABLES: "Regular tables with exactly one partition whose name differs from the table name. Calculated tables and calculation groups are not checked.",
|
|
54
|
+
PERCENTAGE_FORMATTING: "Measures with a percent format string other than `#,0.0%;-#,0.0%;#,0.0%`.",
|
|
55
|
+
PERSPECTIVES_WITH_NO_OBJECTS: "Perspectives that contain no tables. Adding any column, measure, or hierarchy to a perspective adds its table, so a perspective with no tables is empty.",
|
|
56
|
+
PROVIDE_FORMAT_STRING_FOR_MEASURES: "Visible measures with no format string and no dynamic format string.",
|
|
57
|
+
REDUCE_NUMBER_OF_CALCULATED_COLUMNS: "Models with more than five calculated columns across all tables. Columns of calculated tables do not count, and the finding is on the model.",
|
|
58
|
+
REDUCE_USAGE_OF_CALCULATED_COLUMNS_THAT_USE_THE_RELATED_FUNCTION: "Calculated columns whose DAX calls RELATED.",
|
|
59
|
+
REDUCE_USAGE_OF_CALCULATED_TABLES: "Every calculated table. Calculation groups are not included.",
|
|
60
|
+
"REDUCE_USAGE_OF_LONG-LENGTH_COLUMNS_WITH_HIGH_CARDINALITY": "Text columns where more than 500,000 rows hold values longer than 100 characters. Row data is not in the model files, so pbiplint lists this rule but cannot run it.",
|
|
61
|
+
RELATIONSHIP_COLUMNS_SAME_DATA_TYPE: "Relationships whose two columns have different data types.",
|
|
62
|
+
RELATIONSHIP_COLUMNS_SHOULD_BE_OF_INTEGER_DATA_TYPE: "Any column that takes part in a relationship and is not a whole number.",
|
|
63
|
+
"REMOVE_AUTO-DATE_TABLE": "Calculated tables generated by the Auto date/time option, whose names start with DateTableTemplate_ or LocalDateTable_.",
|
|
64
|
+
REMOVE_DATA_SOURCES_NOT_REFERENCED_BY_ANY_PARTITIONS: "Data sources that no partition names and that appear nowhere in any partition's query text.",
|
|
65
|
+
REMOVE_REDUNDANT_COLUMNS_IN_RELATED_TABLES: "Columns that take part in no relationship and share a name with a column on a table that this table relates to from the many side. In practice, a fact table column that duplicates a dimension attribute.",
|
|
66
|
+
REMOVE_ROLES_WITH_NO_MEMBERS: "Roles with no members.",
|
|
67
|
+
SET_ISAVAILABLEINMDX_TO_TRUE_ON_NECESSARY_COLUMNS: "Columns with IsAvailableInMdx set to false that are used to sort another column, appear in a hierarchy or a variation, or sort by another column.",
|
|
68
|
+
SNOWFLAKE_SCHEMA_ARCHITECTURE: "Tables that are on the from side of one relationship and the to side of another, which is what a dimension related to a sub-dimension looks like.",
|
|
69
|
+
SPECIAL_CHARS_IN_OBJECT_NAMES: "Names containing a tab, line feed, or carriage return.",
|
|
70
|
+
SPLIT_DATE_AND_TIME: "DateTime columns holding values that are not at midnight. Row data is not in the model files, so pbiplint lists this rule but cannot run it.",
|
|
71
|
+
TRIM_OBJECT_NAMES: "Names that start or end with a space, across every named object type in the model.",
|
|
72
|
+
UNNECESSARY_COLUMNS: "Hidden columns, or columns in hidden tables, that nothing references: no DAX expression, relationship, hierarchy, sort-by column, row-level security filter, or object-level security rule.",
|
|
73
|
+
UNNECESSARY_MEASURES: "Hidden measures, or measures on hidden tables, that no DAX expression references.",
|
|
74
|
+
"UNPIVOT_PIVOTED_(MONTH)_DATA": "Tables that have a numeric column for each of Jan, Feb, Mar, Apr, May, and Jun, matched as substrings of the column names.",
|
|
75
|
+
USE_THE_DIVIDE_FUNCTION_FOR_DIVISION: "Expressions that use the division operator right after a closing bracket or parenthesis, such as `[Sales] / [Cost]` or `SUM(...) / SUM(...)`. A slash that starts a comment is ignored.",
|
|
76
|
+
USE_THE_TREATAS_FUNCTION_INSTEAD_OF_INTERSECT: "Measures and calculation items that call INTERSECT.",
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=rule-summaries.data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-summaries.data.js","sourceRoot":"","sources":["../../src/rules/rule-summaries.data.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,kGAAkG;AAElG,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAqC;IAC9D,6BAA6B,EAC3B,2LAA2L;IAC7L,qEAAqE,EACnE,oLAAoL;IACtL,wBAAwB,EACtB,qHAAqH;IACvH,8DAA8D,EAC5D,wLAAwL;IAC1L,+BAA+B,EAC7B,6FAA6F;IAC/F,oCAAoC,EAClC,sGAAsG;IACxG,6BAA6B,EAC3B,8JAA8J;IAChK,sDAAsD,EACpD,2GAA2G;IAC7G,iEAAiE,EAC/D,gIAAgI;IAClI,8BAA8B,EAC5B,0MAA0M;IAC5M,sFAAsF,EACpF,iHAAiH;IACnH,gCAAgC,EAAE,oDAAoD;IACtF,4CAA4C,EAC1C,uDAAuD;IACzD,kEAAkE,EAChE,yHAAyH;IAC3H,wDAAwD,EACtD,sHAAsH;IACxH,sCAAsC,EACpC,yEAAyE;IAC3E,uDAAuD,EACrD,iKAAiK;IACnK,uBAAuB,EACrB,yFAAyF;IAC3F,2BAA2B,EACzB,4HAA4H;IAC9H,wBAAwB,EACtB,yIAAyI;IAC3I,gCAAgC,EAC9B,qFAAqF;IACvF,sDAAsD,EAAE,oCAAoC;IAC5F,kDAAkD,EAChD,gFAAgF;IAClF,oBAAoB,EAClB,oGAAoG;IACtG,gCAAgC,EAC9B,8FAA8F;IAChG,2CAA2C,EACzC,kKAAkK;IACpK,oCAAoC,EAClC,0KAA0K;IAC5K,6CAA6C,EAC3C,qJAAqJ;IACvJ,uBAAuB,EACrB,mPAAmP;IACrP,iBAAiB,EACf,0MAA0M;IAC5M,+CAA+C,EAC7C,4FAA4F;IAC9F,kBAAkB,EAChB,ggBAAggB;IAClgB,2CAA2C,EACzC,qNAAqN;IACvN,kCAAkC,EAChC,qJAAqJ;IACvJ,sCAAsC,EACpC,gGAAgG;IAClG,uDAAuD,EACrD,iEAAiE;IACnE,iBAAiB,EACf,yGAAyG;IAC3G,0DAA0D,EACxD,6FAA6F;IAC/F,gEAAgE,EAC9D,oIAAoI;IACtI,oCAAoC,EAClC,4PAA4P;IAC9P,8BAA8B,EAC5B,sIAAsI;IACxI,4CAA4C,EAC1C,oMAAoM;IACtM,oCAAoC,EAClC,mFAAmF;IACrF,wBAAwB,EACtB,yFAAyF;IAC3F,2BAA2B,EACzB,2GAA2G;IAC7G,4CAA4C,EAC1C,qJAAqJ;IACvJ,2BAA2B,EACzB,qHAAqH;IACvH,WAAW,EACT,6OAA6O;IAC/O,kEAAkE,EAChE,6IAA6I;IAC/I,qBAAqB,EACnB,2EAA2E;IAC7E,4BAA4B,EAC1B,0JAA0J;IAC5J,kCAAkC,EAChC,sEAAsE;IACxE,mCAAmC,EACjC,8IAA8I;IAChJ,gEAAgE,EAC9D,6CAA6C;IAC/C,iCAAiC,EAAE,8DAA8D;IACjG,2DAA2D,EACzD,sKAAsK;IACxK,mCAAmC,EAAE,4DAA4D;IACjG,mDAAmD,EACjD,yEAAyE;IAC3E,wBAAwB,EACtB,yHAAyH;IAC3H,oDAAoD,EAClD,6FAA6F;IAC/F,0CAA0C,EACxC,4MAA4M;IAC9M,4BAA4B,EAAE,wBAAwB;IACtD,iDAAiD,EAC/C,mJAAmJ;IACrJ,6BAA6B,EAC3B,mJAAmJ;IACrJ,6BAA6B,EAAE,wDAAwD;IACvF,mBAAmB,EACjB,8IAA8I;IAChJ,iBAAiB,EACf,oFAAoF;IACtF,mBAAmB,EACjB,6LAA6L;IAC/L,oBAAoB,EAClB,mFAAmF;IACrF,8BAA8B,EAC5B,4HAA4H;IAC9H,oCAAoC,EAClC,yLAAyL;IAC3L,6CAA6C,EAC3C,qDAAqD;CACxD,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Indexes } from "../index/build.js";
|
|
2
|
+
import type { Model, Named, SourceLocation } from "../model/types.js";
|
|
3
|
+
export type Category = "Performance" | "Error Prevention" | "DAX Expressions" | "Maintenance" | "Formatting" | "Naming Conventions";
|
|
4
|
+
/** Ranking order of categories (spec section 6). */
|
|
5
|
+
export declare const CATEGORY_ORDER: readonly Category[];
|
|
6
|
+
/** 1 info, 2 warning, 3 error, as in BPARules.json. */
|
|
7
|
+
export type Severity = 1 | 2 | 3;
|
|
8
|
+
export declare const SEVERITY_LABEL: Record<Severity, "info" | "warning" | "error">;
|
|
9
|
+
export type ObjectType = "Model" | "Table" | "CalculatedTable" | "CalculationGroupTable" | "Column" | "CalculatedColumn" | "CalculatedTableColumn" | "Measure" | "Partition" | "Relationship" | "Role" | "TablePermission" | "Perspective" | "Hierarchy" | "Level" | "CalculationItem" | "NamedExpression" | "DataSource" | "File";
|
|
10
|
+
export type RuleStatus = "ported" | "needsLiveModel" | "builtin";
|
|
11
|
+
export interface RuleContext {
|
|
12
|
+
indexes: Indexes;
|
|
13
|
+
}
|
|
14
|
+
/** What a rule returns. `object` is used for ignore annotations and stripped before output. */
|
|
15
|
+
export interface RuleFinding {
|
|
16
|
+
objectType: ObjectType;
|
|
17
|
+
objectName: string;
|
|
18
|
+
location?: SourceLocation;
|
|
19
|
+
detail?: string;
|
|
20
|
+
object?: Named;
|
|
21
|
+
}
|
|
22
|
+
export interface Finding {
|
|
23
|
+
ruleId: string;
|
|
24
|
+
objectType: ObjectType;
|
|
25
|
+
objectName: string;
|
|
26
|
+
location?: SourceLocation;
|
|
27
|
+
detail?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface Rule {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
category: Category;
|
|
33
|
+
severity: Severity;
|
|
34
|
+
scope: ObjectType[];
|
|
35
|
+
/** What the rule checks, one paragraph from the rule page in pbiplint's own words. */
|
|
36
|
+
description: string;
|
|
37
|
+
fixExpression?: string;
|
|
38
|
+
references: string[];
|
|
39
|
+
status: RuleStatus;
|
|
40
|
+
check(model: Model, ctx: RuleContext): RuleFinding[];
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,MAAM,MAAM,QAAQ,GAChB,aAAa,GACb,kBAAkB,GAClB,iBAAiB,GACjB,aAAa,GACb,YAAY,GACZ,oBAAoB,CAAC;AAEzB,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,SAAS,QAAQ,EAO7C,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEjC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIzE,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB,OAAO,GACP,OAAO,GACP,iBAAiB,GACjB,uBAAuB,GACvB,QAAQ,GACR,kBAAkB,GAClB,uBAAuB,GACvB,SAAS,GACT,WAAW,GACX,cAAc,GACd,MAAM,GACN,iBAAiB,GACjB,aAAa,GACb,WAAW,GACX,OAAO,GACP,iBAAiB,GACjB,iBAAiB,GACjB,YAAY,GACZ,MAAM,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,gBAAgB,GAAG,SAAS,CAAC;AAEjE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,sFAAsF;IACtF,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,WAAW,EAAE,CAAC;CACtD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Ranking order of categories (spec section 6). */
|
|
2
|
+
export const CATEGORY_ORDER = [
|
|
3
|
+
"Performance",
|
|
4
|
+
"Error Prevention",
|
|
5
|
+
"DAX Expressions",
|
|
6
|
+
"Maintenance",
|
|
7
|
+
"Formatting",
|
|
8
|
+
"Naming Conventions",
|
|
9
|
+
];
|
|
10
|
+
export const SEVERITY_LABEL = {
|
|
11
|
+
1: "info",
|
|
12
|
+
2: "warning",
|
|
13
|
+
3: "error",
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rules/types.ts"],"names":[],"mappings":"AAWA,oDAAoD;AACpD,MAAM,CAAC,MAAM,cAAc,GAAwB;IACjD,aAAa;IACb,kBAAkB;IAClB,iBAAiB;IACjB,aAAa;IACb,YAAY;IACZ,oBAAoB;CACrB,CAAC;AAKF,MAAM,CAAC,MAAM,cAAc,GAAmD;IAC5E,CAAC,EAAE,MAAM;IACT,CAAC,EAAE,SAAS;IACZ,CAAC,EAAE,OAAO;CACX,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParsedFile } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Generic TMDL tree parser. Unknown object types and properties parse as generic nodes,
|
|
4
|
+
* so a construct this code has never seen never aborts a run.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseTmdl(file: string, text: string): ParsedFile;
|
|
7
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/tmdl/parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAwB,MAAM,YAAY,CAAC;AAuCnE;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAqJhE"}
|