@trebired/code-discipline 4.6.1 → 4.6.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  All notable changes to `@trebired/code-discipline` will be documented here.
4
4
 
5
+ ## 4.6.2
6
+
7
+ - Added a constant-folding pass to `bannedPatterns` for TypeScript/JavaScript files: string concatenation via `+`, template literals with foldable interpolations, `[...literals].join(literalSeparator)`, and same-scope `const` aliases of those are now evaluated at zero runtime cost and checked against configured patterns, closing the "split into literal chunks and glue them" evasion. Expressions touching anything non-literal (a parameter, `process.env`, a function call, a shadowed name) are left unfolded, by design — no guessing, no false positives.
8
+
5
9
  ## 4.6.1
6
10
 
7
11
  - Removed `tsNocheckAudit` (added in `4.6.0`). The rule is gone entirely: no config surface, no CLI selector, no fix pipeline step.
package/README.md CHANGED
@@ -365,6 +365,8 @@ Reports case-insensitive substring matches found in source files.
365
365
  - matching is content-based, not whole-word-only
366
366
  - `allowedFiles` lets specific project-relative files bypass a specific banned pattern
367
367
  - `severity` defaults to `"fail"`
368
+ - for `.ts`/`.tsx`/`.mts`/`.cts`/`.js`/`.jsx`/`.mjs`/`.cjs` files, patterns are also checked against expressions the compiler can constant-fold to a fixed string at zero runtime cost: `+` concatenation of literals, template literals with foldable interpolations, `[...literals].join(literalSeparator)`, and same-scope `const` aliases of those — so `["OPER", "LORN"].join("")` is caught the same as the literal `"OPERLORN"` would be
369
+ - expressions touching anything non-literal (a parameter, `process.env`, a function call, a name shadowed elsewhere in the file) are never folded — this closes the "split into literal chunks" evasion without guessing at genuinely computed values
368
370
 
369
371
  Example:
370
372
 
@@ -0,0 +1,11 @@
1
+ import ts from "typescript";
2
+ type FoldedStringMatchKind = "concat" | "identifier" | "join" | "template";
3
+ type FoldedStringMatch = {
4
+ value: string;
5
+ line: number;
6
+ kind: FoldedStringMatchKind;
7
+ };
8
+ declare function collectFoldedStringMatches(sourceFile: ts.SourceFile): FoldedStringMatch[];
9
+ export { collectFoldedStringMatches };
10
+ export type { FoldedStringMatch, FoldedStringMatchKind };
11
+ //# sourceMappingURL=fold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../../../src/checks/rules/banned/fold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,KAAK,qBAAqB,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,CAAC;AAE3E,KAAK,iBAAiB,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,qBAAqB,CAAC;CAC7B,CAAC;AA2LF,iBAAS,0BAA0B,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,iBAAiB,EAAE,CAsBlF;AAED,OAAO,EAAE,0BAA0B,EAAE,CAAC;AACtC,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,210 @@
1
+ import ts from "typescript";
2
+ function isPlusBinary(node) {
3
+ return ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken;
4
+ }
5
+ function foldArrayOfStrings(node, bindings) {
6
+ if (!ts.isArrayLiteralExpression(node))
7
+ return null;
8
+ const parts = [];
9
+ for (const element of node.elements) {
10
+ const folded = tryFoldExpression(element, bindings);
11
+ if (folded === null)
12
+ return null;
13
+ parts.push(folded);
14
+ }
15
+ return parts;
16
+ }
17
+ function tryFoldJoinCall(node, bindings) {
18
+ if (!ts.isPropertyAccessExpression(node.expression))
19
+ return null;
20
+ if (node.expression.name.text !== "join")
21
+ return null;
22
+ const items = foldArrayOfStrings(node.expression.expression, bindings);
23
+ if (items === null)
24
+ return null;
25
+ if (node.arguments.length > 1)
26
+ return null;
27
+ let separator = ",";
28
+ if (node.arguments.length === 1) {
29
+ const foldedSeparator = tryFoldExpression(node.arguments[0], bindings);
30
+ if (foldedSeparator === null)
31
+ return null;
32
+ separator = foldedSeparator;
33
+ }
34
+ return items.join(separator);
35
+ }
36
+ function tryFoldExpression(node, bindings) {
37
+ if (ts.isParenthesizedExpression(node)) {
38
+ return tryFoldExpression(node.expression, bindings);
39
+ }
40
+ if (ts.isStringLiteralLike(node)) {
41
+ return node.text;
42
+ }
43
+ if (isPlusBinary(node)) {
44
+ const left = tryFoldExpression(node.left, bindings);
45
+ if (left === null)
46
+ return null;
47
+ const right = tryFoldExpression(node.right, bindings);
48
+ if (right === null)
49
+ return null;
50
+ return left + right;
51
+ }
52
+ if (ts.isTemplateExpression(node)) {
53
+ let result = node.head.text;
54
+ for (const span of node.templateSpans) {
55
+ const folded = tryFoldExpression(span.expression, bindings);
56
+ if (folded === null)
57
+ return null;
58
+ result += folded + span.literal.text;
59
+ }
60
+ return result;
61
+ }
62
+ if (ts.isCallExpression(node)) {
63
+ return tryFoldJoinCall(node, bindings);
64
+ }
65
+ if (ts.isIdentifier(node)) {
66
+ return bindings.get(node.text) ?? null;
67
+ }
68
+ return null;
69
+ }
70
+ function isIdentifierValueReference(node) {
71
+ const parent = node.parent;
72
+ if (!parent)
73
+ return true;
74
+ if (ts.isPropertyAccessExpression(parent) && parent.name === node)
75
+ return false;
76
+ if (ts.isPropertyAssignment(parent) && parent.name === node)
77
+ return false;
78
+ if (ts.isVariableDeclaration(parent) && parent.name === node)
79
+ return false;
80
+ if (ts.isBindingElement(parent) && parent.name === node)
81
+ return false;
82
+ if (ts.isParameter(parent) && parent.name === node)
83
+ return false;
84
+ if ((ts.isFunctionDeclaration(parent) || ts.isClassDeclaration(parent) || ts.isFunctionExpression(parent) || ts.isClassExpression(parent)) && parent.name === node)
85
+ return false;
86
+ if (ts.isImportSpecifier(parent) && (parent.name === node || parent.propertyName === node))
87
+ return false;
88
+ if (ts.isImportClause(parent) && parent.name === node)
89
+ return false;
90
+ if (ts.isNamespaceImport(parent) && parent.name === node)
91
+ return false;
92
+ if (ts.isLabeledStatement(parent) && parent.label === node)
93
+ return false;
94
+ if (ts.isBreakOrContinueStatement(parent) && parent.label === node)
95
+ return false;
96
+ if (ts.isTypeReferenceNode(parent) || ts.isTypeQueryNode(parent))
97
+ return false;
98
+ if (ts.isQualifiedName(parent) && parent.right === node)
99
+ return false;
100
+ return true;
101
+ }
102
+ function addBindingName(name, names) {
103
+ if (ts.isIdentifier(name)) {
104
+ names.add(name.text);
105
+ return;
106
+ }
107
+ for (const element of name.elements) {
108
+ if (ts.isBindingElement(element))
109
+ addBindingName(element.name, names);
110
+ }
111
+ }
112
+ function collectNestedDeclaredNames(sourceFile) {
113
+ const names = new Set();
114
+ function visitNested(node) {
115
+ if (ts.isVariableDeclaration(node))
116
+ addBindingName(node.name, names);
117
+ if (ts.isParameter(node))
118
+ addBindingName(node.name, names);
119
+ if (ts.isCatchClause(node) && node.variableDeclaration)
120
+ addBindingName(node.variableDeclaration.name, names);
121
+ if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name)
122
+ names.add(node.name.text);
123
+ if (ts.isImportSpecifier(node))
124
+ names.add(node.name.text);
125
+ if (ts.isImportClause(node) && node.name)
126
+ names.add(node.name.text);
127
+ if (ts.isNamespaceImport(node))
128
+ names.add(node.name.text);
129
+ ts.forEachChild(node, visitNested);
130
+ }
131
+ for (const statement of sourceFile.statements) {
132
+ if (ts.isVariableStatement(statement)) {
133
+ for (const declaration of statement.declarationList.declarations) {
134
+ if (declaration.initializer)
135
+ visitNested(declaration.initializer);
136
+ }
137
+ continue;
138
+ }
139
+ visitNested(statement);
140
+ }
141
+ return names;
142
+ }
143
+ function collectTopLevelSafeConstBindings(sourceFile) {
144
+ const candidates = [];
145
+ for (const statement of sourceFile.statements) {
146
+ if (!ts.isVariableStatement(statement))
147
+ continue;
148
+ if ((ts.getCombinedNodeFlags(statement.declarationList) & ts.NodeFlags.Const) === 0)
149
+ continue;
150
+ for (const declaration of statement.declarationList.declarations) {
151
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer)
152
+ continue;
153
+ candidates.push({ name: declaration.name.text, initializer: declaration.initializer });
154
+ }
155
+ }
156
+ const bindings = new Map();
157
+ let changed = true;
158
+ while (changed) {
159
+ changed = false;
160
+ for (const candidate of candidates) {
161
+ if (bindings.has(candidate.name))
162
+ continue;
163
+ const folded = tryFoldExpression(candidate.initializer, bindings);
164
+ if (folded !== null) {
165
+ bindings.set(candidate.name, folded);
166
+ changed = true;
167
+ }
168
+ }
169
+ }
170
+ for (const shadowedName of collectNestedDeclaredNames(sourceFile)) {
171
+ bindings.delete(shadowedName);
172
+ }
173
+ return bindings;
174
+ }
175
+ function resolveMatchKind(node) {
176
+ if (ts.isCallExpression(node))
177
+ return "join";
178
+ if (ts.isTemplateExpression(node))
179
+ return "template";
180
+ if (ts.isIdentifier(node))
181
+ return "identifier";
182
+ return "concat";
183
+ }
184
+ function isFoldableSiteCandidate(node, bindings) {
185
+ if (ts.isCallExpression(node) || ts.isTemplateExpression(node) || isPlusBinary(node))
186
+ return true;
187
+ return ts.isIdentifier(node) && bindings.has(node.text) && isIdentifierValueReference(node);
188
+ }
189
+ function collectFoldedStringMatches(sourceFile) {
190
+ const bindings = collectTopLevelSafeConstBindings(sourceFile);
191
+ const matches = [];
192
+ function visit(node) {
193
+ if (isFoldableSiteCandidate(node, bindings)) {
194
+ const folded = tryFoldExpression(node, bindings);
195
+ if (folded !== null) {
196
+ matches.push({
197
+ value: folded,
198
+ line: sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1,
199
+ kind: resolveMatchKind(node),
200
+ });
201
+ return;
202
+ }
203
+ }
204
+ ts.forEachChild(node, visit);
205
+ }
206
+ visit(sourceFile);
207
+ return matches;
208
+ }
209
+ export { collectFoldedStringMatches };
210
+ //# sourceMappingURL=fold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fold.js","sourceRoot":"","sources":["../../../../src/checks/rules/banned/fold.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../../src/checks/rules/banned/patterns.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAmBnF,iBAAe,8BAA8B,CAC3C,WAAW,EAAE,iBAAiB,EAAE,EAChC,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAuCpC;AAED,OAAO,EAAE,8BAA8B,EAAE,CAAC"}
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../../src/checks/rules/banned/patterns.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,gBAAgB,CAAC;AAC3E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAiDnF,iBAAe,8BAA8B,CAC3C,WAAW,EAAE,iBAAiB,EAAE,EAChC,OAAO,EAAE,oCAAoC,GAC5C,OAAO,CAAC,uBAAuB,EAAE,CAAC,CA6CpC;AAED,OAAO,EAAE,8BAA8B,EAAE,CAAC"}
@@ -1,5 +1,9 @@
1
1
  import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseSource } from "../../../imports/module-specifiers.js";
4
+ import { isTypeScriptFamilyExtension } from "../../../shared/languages.js";
2
5
  import { createRuleProgress, emitRuleChunk, emitRuleCompleted } from "../../progress.js";
6
+ import { collectFoldedStringMatches } from "./fold.js";
3
7
  function countOccurrences(text, pattern) {
4
8
  if (!pattern)
5
9
  return 0;
@@ -14,6 +18,28 @@ function countOccurrences(text, pattern) {
14
18
  }
15
19
  return count;
16
20
  }
21
+ function collectFoldedMatchesSafely(text, filePath) {
22
+ if (!isTypeScriptFamilyExtension(path.extname(filePath).toLowerCase()))
23
+ return [];
24
+ try {
25
+ return collectFoldedStringMatches(parseSource(text, filePath));
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ }
31
+ function formatOccurrenceSuffix(count) {
32
+ return count > 1 ? ` ${count} times` : "";
33
+ }
34
+ function buildBannedPatternMessage(patternValue, rawOccurrences, foldedOccurrences) {
35
+ if (foldedOccurrences === 0) {
36
+ return `file contains banned pattern "${patternValue}"${formatOccurrenceSuffix(rawOccurrences)}`;
37
+ }
38
+ if (rawOccurrences === 0) {
39
+ return `file contains banned pattern "${patternValue}" via a constant-folded expression${formatOccurrenceSuffix(foldedOccurrences)}`;
40
+ }
41
+ return `file contains banned pattern "${patternValue}"${formatOccurrenceSuffix(rawOccurrences)} and via a constant-folded expression${formatOccurrenceSuffix(foldedOccurrences)}`;
42
+ }
17
43
  async function collectBannedPatternViolations(sourceFiles, options) {
18
44
  const rule = options.rules.bannedPatterns;
19
45
  if (!rule)
@@ -28,20 +54,24 @@ async function collectBannedPatternViolations(sourceFiles, options) {
28
54
  const file = sourceFiles[index];
29
55
  const text = await fs.readFile(file.absolutePath, "utf8");
30
56
  const normalizedText = text.toLowerCase();
57
+ const foldedMatches = collectFoldedMatchesSafely(text, file.absolutePath);
31
58
  for (const pattern of rule.patterns) {
32
59
  if (pattern.allowedFiles.includes(file.relativeFromProjectRoot))
33
60
  continue;
34
- if (!normalizedText.includes(pattern.normalizedValue))
61
+ const rawOccurrences = normalizedText.includes(pattern.normalizedValue) ? countOccurrences(normalizedText, pattern.normalizedValue) : 0;
62
+ const patternFoldedMatches = foldedMatches.filter((match) => match.value.toLowerCase().includes(pattern.normalizedValue));
63
+ if (rawOccurrences === 0 && patternFoldedMatches.length === 0)
35
64
  continue;
36
- const occurrences = countOccurrences(normalizedText, pattern.normalizedValue);
37
65
  violations.push({
38
66
  rule: "banned-patterns",
39
67
  fix: false,
40
68
  filePath: file.relativeFromProjectRoot,
41
- message: `file contains banned pattern "${pattern.value}"${occurrences > 1 ? ` ${occurrences} times` : ""}`,
69
+ message: buildBannedPatternMessage(pattern.value, rawOccurrences, patternFoldedMatches.length),
42
70
  details: {
43
71
  pattern: pattern.value,
44
- occurrences,
72
+ occurrences: rawOccurrences,
73
+ foldedOccurrences: patternFoldedMatches.length,
74
+ foldedMatches: patternFoldedMatches.map((match) => ({ line: match.line, kind: match.kind })),
45
75
  allowedFiles: pattern.allowedFiles,
46
76
  },
47
77
  });
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../../src/checks/rules/banned/patterns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAKlC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEzF,SAAS,gBAAgB,CAAC,IAAY,EAAE,OAAe;IACrD,IAAI,CAAC,OAAO;QAAE,OAAO,CAAC,CAAC;IAEvB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,OAAO,KAAK,IAAI,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAChD,IAAI,UAAU,GAAG,CAAC;YAAE,MAAM;QAC1B,KAAK,IAAI,CAAC,CAAC;QACX,KAAK,GAAG,UAAU,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,KAAK,UAAU,8BAA8B,CAC3C,WAAgC,EAChC,OAA6C;IAE7C,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC;IAC1C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IAErB,MAAM,UAAU,GAA8B,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAG,kBAAkB,CAAC;QAClC,QAAQ,EAAE,OAAO,CAAC,gBAAgB;QAClC,IAAI,EAAE,iBAAiB;QACvB,UAAU,EAAE,WAAW,CAAC,MAAM;KAC/B,CAAC,CAAC;IAEH,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAE,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC1D,MAAM,cAAc,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAE1C,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,IAAI,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,uBAAuB,CAAC;gBAAE,SAAS;YAC1E,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC;gBAAE,SAAS;YAEhE,MAAM,WAAW,GAAG,gBAAgB,CAAC,cAAc,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;YAC9E,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,iBAAiB;gBACvB,GAAG,EAAE,KAAK;gBACV,QAAQ,EAAE,IAAI,CAAC,uBAAuB;gBACtC,OAAO,EAAE,iCAAiC,OAAO,CAAC,KAAK,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,WAAW,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC3G,OAAO,EAAE;oBACP,OAAO,EAAE,OAAO,CAAC,KAAK;oBACtB,WAAW;oBACX,YAAY,EAAE,OAAO,CAAC,YAAY;iBACnC;aACF,CAAC,CAAC;QACL,CAAC;QAED,aAAa,CAAC,QAAQ,EAAE,KAAK,GAAG,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IACxD,CAAC;IAED,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC/C,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,OAAO,EAAE,8BAA8B,EAAE,CAAC"}
1
+ {"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../../src/checks/rules/banned/patterns.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebired/code-discipline",
3
- "version": "4.6.1",
3
+ "version": "4.6.2",
4
4
  "description": "Configurable codebase discipline checks and rule-driven fixes for Bun and Node.js projects.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "keywords": [