@brandlab-digital/conformance 2.1.0 → 3.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/dist/analytics-e2e.d.ts +52 -0
- package/dist/analytics-e2e.d.ts.map +1 -0
- package/dist/analytics-e2e.js +189 -0
- package/dist/analytics-e2e.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +40 -3
- package/dist/cli.js.map +1 -1
- package/dist/contact-e2e.d.ts +43 -0
- package/dist/contact-e2e.d.ts.map +1 -0
- package/dist/contact-e2e.js +193 -0
- package/dist/contact-e2e.js.map +1 -0
- package/dist/current-release.js +7 -7
- package/dist/current-release.js.map +1 -1
- package/dist/e2e-support.d.ts +42 -0
- package/dist/e2e-support.d.ts.map +1 -0
- package/dist/e2e-support.js +126 -0
- package/dist/e2e-support.js.map +1 -0
- package/dist/engineering.d.ts +14 -0
- package/dist/engineering.d.ts.map +1 -1
- package/dist/engineering.js +446 -50
- package/dist/engineering.js.map +1 -1
- package/dist/estate.d.ts +105 -0
- package/dist/estate.d.ts.map +1 -0
- package/dist/estate.js +391 -0
- package/dist/estate.js.map +1 -0
- package/dist/families-e2e.d.ts +45 -0
- package/dist/families-e2e.d.ts.map +1 -0
- package/dist/families-e2e.js +248 -0
- package/dist/families-e2e.js.map +1 -0
- package/dist/glob.d.ts +21 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +138 -0
- package/dist/glob.js.map +1 -0
- package/dist/knip.d.ts +47 -0
- package/dist/knip.d.ts.map +1 -0
- package/dist/knip.js +84 -0
- package/dist/knip.js.map +1 -0
- package/dist/project.d.ts +4 -1
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +163 -147
- package/dist/project.js.map +1 -1
- package/dist/public-copy.d.ts +36 -0
- package/dist/public-copy.d.ts.map +1 -0
- package/dist/public-copy.js +517 -0
- package/dist/public-copy.js.map +1 -0
- package/dist/rule-registry.d.ts +292 -0
- package/dist/rule-registry.d.ts.map +1 -0
- package/dist/rule-registry.js +68 -0
- package/dist/rule-registry.js.map +1 -0
- package/dist/search-certification.d.ts +4 -0
- package/dist/search-certification.d.ts.map +1 -1
- package/dist/search-certification.js +17 -3
- package/dist/search-certification.js.map +1 -1
- package/dist/source-files.d.ts +54 -0
- package/dist/source-files.d.ts.map +1 -0
- package/dist/source-files.js +323 -0
- package/dist/source-files.js.map +1 -0
- package/dist/static-rules.d.ts +45 -0
- package/dist/static-rules.d.ts.map +1 -0
- package/dist/static-rules.js +762 -0
- package/dist/static-rules.js.map +1 -0
- package/dist/verification-policy.d.ts +13 -4
- package/dist/verification-policy.d.ts.map +1 -1
- package/dist/verification-policy.js +31 -12
- package/dist/verification-policy.js.map +1 -1
- package/package.json +37 -1
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { join, posix } from "node:path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
/** Directory roots that can hold project-owned application source. */
|
|
5
|
+
export const BRANDLAB_SOURCE_ROOT_DIRECTORIES = [
|
|
6
|
+
"src",
|
|
7
|
+
"app",
|
|
8
|
+
"components",
|
|
9
|
+
"lib",
|
|
10
|
+
"config",
|
|
11
|
+
"hooks",
|
|
12
|
+
"context",
|
|
13
|
+
"utils",
|
|
14
|
+
"data",
|
|
15
|
+
];
|
|
16
|
+
const SOURCE_FILE_PATTERN = /\.[cm]?[jt]sx?$/;
|
|
17
|
+
const TEST_FILE_PATTERN = /\.(?:test|spec)\.[cm]?[jt]sx?$/;
|
|
18
|
+
const ROOT_SOURCE_FILE_PATTERN = /^(?:instrumentation[\w.-]*|proxy|middleware|next\.config|mdx-components)\.[cm]?[jt]sx?$/;
|
|
19
|
+
const RESOLUTION_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
20
|
+
/** Collect every non-test source file under the recognised project roots. */
|
|
21
|
+
export function collectBrandlabSource(root) {
|
|
22
|
+
if (!isDirectory(root)) {
|
|
23
|
+
return { root, directoryRoots: [], rootFiles: [], files: [], byPath: new Map() };
|
|
24
|
+
}
|
|
25
|
+
const directoryRoots = BRANDLAB_SOURCE_ROOT_DIRECTORIES.filter((name) => isDirectory(join(root, name)));
|
|
26
|
+
const rootFiles = readdirSync(root, { withFileTypes: true })
|
|
27
|
+
.filter((entry) => entry.isFile() && ROOT_SOURCE_FILE_PATTERN.test(entry.name))
|
|
28
|
+
.map((entry) => entry.name)
|
|
29
|
+
.sort();
|
|
30
|
+
const paths = [
|
|
31
|
+
...directoryRoots.flatMap((name) => walkSourceFiles(root, name)),
|
|
32
|
+
...rootFiles,
|
|
33
|
+
].sort();
|
|
34
|
+
const files = paths.map((path) => {
|
|
35
|
+
const text = readFileSync(join(root, path), "utf8");
|
|
36
|
+
return { path, text, executable: stripCommentsPreservingLayout(text) };
|
|
37
|
+
});
|
|
38
|
+
return {
|
|
39
|
+
root,
|
|
40
|
+
directoryRoots: [...directoryRoots],
|
|
41
|
+
rootFiles,
|
|
42
|
+
files,
|
|
43
|
+
byPath: new Map(files.map((file) => [file.path, file])),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** List every file below a project-relative directory, skipping dependencies and dot entries. */
|
|
47
|
+
export function walkProjectFiles(root, directory) {
|
|
48
|
+
const absolute = join(root, directory);
|
|
49
|
+
if (!isDirectory(absolute))
|
|
50
|
+
return [];
|
|
51
|
+
const files = [];
|
|
52
|
+
for (const entry of readdirSync(absolute, { withFileTypes: true })) {
|
|
53
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.isSymbolicLink())
|
|
54
|
+
continue;
|
|
55
|
+
const path = posix.join(directory, entry.name);
|
|
56
|
+
if (entry.isDirectory())
|
|
57
|
+
files.push(...walkProjectFiles(root, path));
|
|
58
|
+
else if (entry.isFile())
|
|
59
|
+
files.push(path);
|
|
60
|
+
}
|
|
61
|
+
return files.sort();
|
|
62
|
+
}
|
|
63
|
+
function walkSourceFiles(root, directory) {
|
|
64
|
+
return walkProjectFiles(root, directory).filter((path) => {
|
|
65
|
+
const name = posix.basename(path);
|
|
66
|
+
return SOURCE_FILE_PATTERN.test(name) && !TEST_FILE_PATTERN.test(name);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const parsedFiles = new WeakMap();
|
|
70
|
+
export function parseBrandlabSourceFile(file) {
|
|
71
|
+
const cached = parsedFiles.get(file);
|
|
72
|
+
if (cached)
|
|
73
|
+
return cached;
|
|
74
|
+
const parsed = ts.createSourceFile(file.path, file.text, ts.ScriptTarget.Latest, true, scriptKind(file.path));
|
|
75
|
+
parsedFiles.set(file, parsed);
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
function scriptKind(path) {
|
|
79
|
+
if (path.endsWith(".tsx"))
|
|
80
|
+
return ts.ScriptKind.TSX;
|
|
81
|
+
if (path.endsWith(".jsx"))
|
|
82
|
+
return ts.ScriptKind.JSX;
|
|
83
|
+
if (/\.[cm]?js$/.test(path))
|
|
84
|
+
return ts.ScriptKind.JS;
|
|
85
|
+
return ts.ScriptKind.TS;
|
|
86
|
+
}
|
|
87
|
+
/** Static imports, re-exports and literal dynamic imports declared by one file. */
|
|
88
|
+
export function listModuleReferences(file) {
|
|
89
|
+
const source = parseBrandlabSourceFile(file);
|
|
90
|
+
const references = [];
|
|
91
|
+
const lineOf = (node) => source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
|
|
92
|
+
for (const statement of source.statements) {
|
|
93
|
+
if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
94
|
+
const clause = statement.importClause;
|
|
95
|
+
const bindings = [];
|
|
96
|
+
if (clause?.name)
|
|
97
|
+
bindings.push({ imported: "default", local: clause.name.text });
|
|
98
|
+
const named = clause?.namedBindings;
|
|
99
|
+
if (named && ts.isNamespaceImport(named))
|
|
100
|
+
bindings.push({ imported: "*", local: named.name.text });
|
|
101
|
+
if (named && ts.isNamedImports(named)) {
|
|
102
|
+
for (const element of named.elements) {
|
|
103
|
+
bindings.push({ imported: (element.propertyName ?? element.name).text, local: element.name.text });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
references.push({
|
|
107
|
+
specifier: statement.moduleSpecifier.text,
|
|
108
|
+
kind: "import",
|
|
109
|
+
typeOnly: Boolean(clause?.isTypeOnly),
|
|
110
|
+
bindings,
|
|
111
|
+
line: lineOf(statement),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (ts.isExportDeclaration(statement) &&
|
|
115
|
+
statement.moduleSpecifier &&
|
|
116
|
+
ts.isStringLiteral(statement.moduleSpecifier)) {
|
|
117
|
+
const clause = statement.exportClause;
|
|
118
|
+
const bindings = clause && ts.isNamedExports(clause)
|
|
119
|
+
? clause.elements.map((element) => ({
|
|
120
|
+
imported: (element.propertyName ?? element.name).text,
|
|
121
|
+
local: element.name.text,
|
|
122
|
+
}))
|
|
123
|
+
: [{ imported: "*", local: "*" }];
|
|
124
|
+
references.push({
|
|
125
|
+
specifier: statement.moduleSpecifier.text,
|
|
126
|
+
kind: "export",
|
|
127
|
+
typeOnly: statement.isTypeOnly,
|
|
128
|
+
bindings,
|
|
129
|
+
line: lineOf(statement),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const visit = (node) => {
|
|
134
|
+
if (ts.isCallExpression(node) &&
|
|
135
|
+
node.expression.kind === ts.SyntaxKind.ImportKeyword &&
|
|
136
|
+
node.arguments[0] &&
|
|
137
|
+
ts.isStringLiteralLike(node.arguments[0])) {
|
|
138
|
+
references.push({
|
|
139
|
+
specifier: node.arguments[0].text,
|
|
140
|
+
kind: "dynamic",
|
|
141
|
+
typeOnly: false,
|
|
142
|
+
bindings: [],
|
|
143
|
+
line: lineOf(node),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
ts.forEachChild(node, visit);
|
|
147
|
+
};
|
|
148
|
+
visit(source);
|
|
149
|
+
return references;
|
|
150
|
+
}
|
|
151
|
+
/** Local names bound to an export of a package module, for example BrandlabGoogleTag. */
|
|
152
|
+
export function importedLocalNames(file, modulePattern, importedName) {
|
|
153
|
+
return listModuleReferences(file)
|
|
154
|
+
.filter((reference) => reference.kind === "import" && modulePattern.test(reference.specifier))
|
|
155
|
+
.flatMap((reference) => reference.bindings)
|
|
156
|
+
.filter((binding) => binding.imported === importedName)
|
|
157
|
+
.map((binding) => binding.local);
|
|
158
|
+
}
|
|
159
|
+
/** Calls to a named export of a package module, following local import aliases. */
|
|
160
|
+
export function findImportedCalls(file, modulePattern, importedName) {
|
|
161
|
+
const locals = new Set(importedLocalNames(file, modulePattern, importedName));
|
|
162
|
+
if (locals.size === 0)
|
|
163
|
+
return [];
|
|
164
|
+
const calls = [];
|
|
165
|
+
const visit = (node) => {
|
|
166
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && locals.has(node.expression.text)) {
|
|
167
|
+
calls.push(node);
|
|
168
|
+
}
|
|
169
|
+
ts.forEachChild(node, visit);
|
|
170
|
+
};
|
|
171
|
+
visit(parseBrandlabSourceFile(file));
|
|
172
|
+
return calls;
|
|
173
|
+
}
|
|
174
|
+
export function lineOfNode(file, node) {
|
|
175
|
+
const source = parseBrandlabSourceFile(file);
|
|
176
|
+
return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
|
|
177
|
+
}
|
|
178
|
+
/** The value of a named property in an object literal, or undefined. */
|
|
179
|
+
export function objectPropertyValue(node, name) {
|
|
180
|
+
if (!node || !ts.isObjectLiteralExpression(node))
|
|
181
|
+
return undefined;
|
|
182
|
+
for (const property of node.properties) {
|
|
183
|
+
if (!ts.isPropertyAssignment(property))
|
|
184
|
+
continue;
|
|
185
|
+
const key = property.name;
|
|
186
|
+
const keyText = ts.isIdentifier(key) || ts.isStringLiteralLike(key) ? key.text : null;
|
|
187
|
+
if (keyText === name)
|
|
188
|
+
return property.initializer;
|
|
189
|
+
}
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
export function stringLiteralValue(node) {
|
|
193
|
+
return node && ts.isStringLiteralLike(node) ? node.text : null;
|
|
194
|
+
}
|
|
195
|
+
/** Resolve project-local specifiers (relative and tsconfig path aliases) to indexed files. */
|
|
196
|
+
export function createBrandlabModuleResolver(root, knownPaths) {
|
|
197
|
+
const aliases = readPathAliases(root);
|
|
198
|
+
const candidates = (base) => [
|
|
199
|
+
base,
|
|
200
|
+
...RESOLUTION_EXTENSIONS.map((extension) => `${base}${extension}`),
|
|
201
|
+
...RESOLUTION_EXTENSIONS.map((extension) => posix.join(base, `index${extension}`)),
|
|
202
|
+
...(base.endsWith(".js") ? RESOLUTION_EXTENSIONS.map((extension) => base.replace(/\.js$/, extension)) : []),
|
|
203
|
+
];
|
|
204
|
+
const find = (base) => {
|
|
205
|
+
const normalised = posix.normalize(base).replace(/^\.\//, "");
|
|
206
|
+
return candidates(normalised).find((candidate) => knownPaths.has(candidate)) ?? null;
|
|
207
|
+
};
|
|
208
|
+
return (fromPath, specifier) => {
|
|
209
|
+
if (specifier.startsWith("."))
|
|
210
|
+
return find(posix.join(posix.dirname(fromPath), specifier));
|
|
211
|
+
for (const alias of aliases) {
|
|
212
|
+
if (alias.exact ? specifier !== alias.prefix : !specifier.startsWith(alias.prefix))
|
|
213
|
+
continue;
|
|
214
|
+
const rest = alias.exact ? "" : specifier.slice(alias.prefix.length);
|
|
215
|
+
for (const target of alias.targets) {
|
|
216
|
+
const resolved = find(target.replace("*", rest));
|
|
217
|
+
if (resolved)
|
|
218
|
+
return resolved;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
function readPathAliases(root) {
|
|
225
|
+
const fallback = [{ prefix: "@/", exact: false, targets: ["src/*"] }];
|
|
226
|
+
const configPath = join(root, "tsconfig.json");
|
|
227
|
+
if (!existsSync(configPath))
|
|
228
|
+
return fallback;
|
|
229
|
+
const parsed = ts.readConfigFile(configPath, (path) => readFileSync(path, "utf8"));
|
|
230
|
+
const options = parsed.config?.compilerOptions;
|
|
231
|
+
if (!options?.paths)
|
|
232
|
+
return fallback;
|
|
233
|
+
const baseUrl = posix.normalize((options.baseUrl ?? ".").replaceAll("\\", "/"));
|
|
234
|
+
return Object.entries(options.paths)
|
|
235
|
+
.filter(([, targets]) => Array.isArray(targets))
|
|
236
|
+
.map(([pattern, targets]) => ({
|
|
237
|
+
prefix: pattern.replace(/\*$/, ""),
|
|
238
|
+
exact: !pattern.endsWith("*"),
|
|
239
|
+
targets: targets.map((target) => posix.normalize(posix.join(baseUrl, target.replaceAll("\\", "/")))),
|
|
240
|
+
}));
|
|
241
|
+
}
|
|
242
|
+
/** Blank comments while preserving string contents, offsets and line breaks. */
|
|
243
|
+
export function stripCommentsPreservingLayout(source) {
|
|
244
|
+
const output = [...source];
|
|
245
|
+
let state = "code";
|
|
246
|
+
let escaped = false;
|
|
247
|
+
for (let index = 0; index < output.length; index += 1) {
|
|
248
|
+
const current = output[index];
|
|
249
|
+
const next = output[index + 1];
|
|
250
|
+
if (state === "line") {
|
|
251
|
+
if (current === "\n" || current === "\r") {
|
|
252
|
+
state = "code";
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
output[index] = " ";
|
|
256
|
+
}
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (state === "block") {
|
|
260
|
+
if (current === "*" && next === "/") {
|
|
261
|
+
output[index] = " ";
|
|
262
|
+
output[index + 1] = " ";
|
|
263
|
+
index += 1;
|
|
264
|
+
state = "code";
|
|
265
|
+
}
|
|
266
|
+
else if (current !== "\n" && current !== "\r") {
|
|
267
|
+
output[index] = " ";
|
|
268
|
+
}
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (state !== "code") {
|
|
272
|
+
if (escaped) {
|
|
273
|
+
escaped = false;
|
|
274
|
+
}
|
|
275
|
+
else if (current === "\\") {
|
|
276
|
+
escaped = true;
|
|
277
|
+
}
|
|
278
|
+
else if ((state === "single" && current === "'") ||
|
|
279
|
+
(state === "double" && current === '"') ||
|
|
280
|
+
(state === "template" && current === "`")) {
|
|
281
|
+
state = "code";
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
if (current === "'") {
|
|
286
|
+
state = "single";
|
|
287
|
+
}
|
|
288
|
+
else if (current === '"') {
|
|
289
|
+
state = "double";
|
|
290
|
+
}
|
|
291
|
+
else if (current === "`") {
|
|
292
|
+
state = "template";
|
|
293
|
+
}
|
|
294
|
+
else if (current === "/" && next === "/") {
|
|
295
|
+
output[index] = " ";
|
|
296
|
+
output[index + 1] = " ";
|
|
297
|
+
index += 1;
|
|
298
|
+
state = "line";
|
|
299
|
+
}
|
|
300
|
+
else if (current === "/" && next === "*") {
|
|
301
|
+
output[index] = " ";
|
|
302
|
+
output[index + 1] = " ";
|
|
303
|
+
index += 1;
|
|
304
|
+
state = "block";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return output.join("");
|
|
308
|
+
}
|
|
309
|
+
export function lineAt(source, index) {
|
|
310
|
+
return source.slice(0, index).split("\n").length;
|
|
311
|
+
}
|
|
312
|
+
export function isDirectory(path) {
|
|
313
|
+
try {
|
|
314
|
+
return lstatSync(path).isDirectory();
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
export function readOptionalText(path) {
|
|
321
|
+
return existsSync(path) ? readFileSync(path, "utf8") : null;
|
|
322
|
+
}
|
|
323
|
+
//# sourceMappingURL=source-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-files.js","sourceRoot":"","sources":["../src/source-files.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, readFileSync, readdirSync } from \"node:fs\";\nimport { join, posix } from \"node:path\";\nimport ts from \"typescript\";\n\n/** Directory roots that can hold project-owned application source. */\nexport const BRANDLAB_SOURCE_ROOT_DIRECTORIES = [\n \"src\",\n \"app\",\n \"components\",\n \"lib\",\n \"config\",\n \"hooks\",\n \"context\",\n \"utils\",\n \"data\",\n] as const;\n\nconst SOURCE_FILE_PATTERN = /\\.[cm]?[jt]sx?$/;\nconst TEST_FILE_PATTERN = /\\.(?:test|spec)\\.[cm]?[jt]sx?$/;\nconst ROOT_SOURCE_FILE_PATTERN =\n /^(?:instrumentation[\\w.-]*|proxy|middleware|next\\.config|mdx-components)\\.[cm]?[jt]sx?$/;\nconst RESOLUTION_EXTENSIONS = [\".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\"];\n\nexport interface BrandlabSourceFile {\n /** Project-relative path with forward slashes. */\n path: string;\n text: string;\n /** The same text with comments blanked so offsets and lines are preserved. */\n executable: string;\n}\n\nexport interface BrandlabSourceIndex {\n root: string;\n /** Recognised directory roots present in the project. */\n directoryRoots: string[];\n /** Recognised root-level framework files present in the project. */\n rootFiles: string[];\n files: BrandlabSourceFile[];\n byPath: ReadonlyMap<string, BrandlabSourceFile>;\n}\n\nexport interface BrandlabImportBinding {\n imported: string;\n local: string;\n}\n\nexport interface BrandlabModuleReference {\n specifier: string;\n kind: \"import\" | \"export\" | \"dynamic\";\n typeOnly: boolean;\n bindings: BrandlabImportBinding[];\n line: number;\n}\n\n/** Collect every non-test source file under the recognised project roots. */\nexport function collectBrandlabSource(root: string): BrandlabSourceIndex {\n if (!isDirectory(root)) {\n return { root, directoryRoots: [], rootFiles: [], files: [], byPath: new Map() };\n }\n const directoryRoots = BRANDLAB_SOURCE_ROOT_DIRECTORIES.filter((name) => isDirectory(join(root, name)));\n const rootFiles = readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isFile() && ROOT_SOURCE_FILE_PATTERN.test(entry.name))\n .map((entry) => entry.name)\n .sort();\n const paths = [\n ...directoryRoots.flatMap((name) => walkSourceFiles(root, name)),\n ...rootFiles,\n ].sort();\n const files = paths.map((path) => {\n const text = readFileSync(join(root, path), \"utf8\");\n return { path, text, executable: stripCommentsPreservingLayout(text) };\n });\n return {\n root,\n directoryRoots: [...directoryRoots],\n rootFiles,\n files,\n byPath: new Map(files.map((file) => [file.path, file])),\n };\n}\n\n/** List every file below a project-relative directory, skipping dependencies and dot entries. */\nexport function walkProjectFiles(root: string, directory: string): string[] {\n const absolute = join(root, directory);\n if (!isDirectory(absolute)) return [];\n const files: string[] = [];\n for (const entry of readdirSync(absolute, { withFileTypes: true })) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\" || entry.isSymbolicLink()) continue;\n const path = posix.join(directory, entry.name);\n if (entry.isDirectory()) files.push(...walkProjectFiles(root, path));\n else if (entry.isFile()) files.push(path);\n }\n return files.sort();\n}\n\nfunction walkSourceFiles(root: string, directory: string): string[] {\n return walkProjectFiles(root, directory).filter((path) => {\n const name = posix.basename(path);\n return SOURCE_FILE_PATTERN.test(name) && !TEST_FILE_PATTERN.test(name);\n });\n}\n\nconst parsedFiles = new WeakMap<BrandlabSourceFile, ts.SourceFile>();\n\nexport function parseBrandlabSourceFile(file: BrandlabSourceFile): ts.SourceFile {\n const cached = parsedFiles.get(file);\n if (cached) return cached;\n const parsed = ts.createSourceFile(file.path, file.text, ts.ScriptTarget.Latest, true, scriptKind(file.path));\n parsedFiles.set(file, parsed);\n return parsed;\n}\n\nfunction scriptKind(path: string): ts.ScriptKind {\n if (path.endsWith(\".tsx\")) return ts.ScriptKind.TSX;\n if (path.endsWith(\".jsx\")) return ts.ScriptKind.JSX;\n if (/\\.[cm]?js$/.test(path)) return ts.ScriptKind.JS;\n return ts.ScriptKind.TS;\n}\n\n/** Static imports, re-exports and literal dynamic imports declared by one file. */\nexport function listModuleReferences(file: BrandlabSourceFile): BrandlabModuleReference[] {\n const source = parseBrandlabSourceFile(file);\n const references: BrandlabModuleReference[] = [];\n const lineOf = (node: ts.Node) => source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;\n for (const statement of source.statements) {\n if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {\n const clause = statement.importClause;\n const bindings: BrandlabImportBinding[] = [];\n if (clause?.name) bindings.push({ imported: \"default\", local: clause.name.text });\n const named = clause?.namedBindings;\n if (named && ts.isNamespaceImport(named)) bindings.push({ imported: \"*\", local: named.name.text });\n if (named && ts.isNamedImports(named)) {\n for (const element of named.elements) {\n bindings.push({ imported: (element.propertyName ?? element.name).text, local: element.name.text });\n }\n }\n references.push({\n specifier: statement.moduleSpecifier.text,\n kind: \"import\",\n typeOnly: Boolean(clause?.isTypeOnly),\n bindings,\n line: lineOf(statement),\n });\n } else if (\n ts.isExportDeclaration(statement) &&\n statement.moduleSpecifier &&\n ts.isStringLiteral(statement.moduleSpecifier)\n ) {\n const clause = statement.exportClause;\n const bindings = clause && ts.isNamedExports(clause)\n ? clause.elements.map((element) => ({\n imported: (element.propertyName ?? element.name).text,\n local: element.name.text,\n }))\n : [{ imported: \"*\", local: \"*\" }];\n references.push({\n specifier: statement.moduleSpecifier.text,\n kind: \"export\",\n typeOnly: statement.isTypeOnly,\n bindings,\n line: lineOf(statement),\n });\n }\n }\n const visit = (node: ts.Node) => {\n if (\n ts.isCallExpression(node) &&\n node.expression.kind === ts.SyntaxKind.ImportKeyword &&\n node.arguments[0] &&\n ts.isStringLiteralLike(node.arguments[0])\n ) {\n references.push({\n specifier: node.arguments[0].text,\n kind: \"dynamic\",\n typeOnly: false,\n bindings: [],\n line: lineOf(node),\n });\n }\n ts.forEachChild(node, visit);\n };\n visit(source);\n return references;\n}\n\n/** Local names bound to an export of a package module, for example BrandlabGoogleTag. */\nexport function importedLocalNames(\n file: BrandlabSourceFile,\n modulePattern: RegExp,\n importedName: string,\n): string[] {\n return listModuleReferences(file)\n .filter((reference) => reference.kind === \"import\" && modulePattern.test(reference.specifier))\n .flatMap((reference) => reference.bindings)\n .filter((binding) => binding.imported === importedName)\n .map((binding) => binding.local);\n}\n\n/** Calls to a named export of a package module, following local import aliases. */\nexport function findImportedCalls(\n file: BrandlabSourceFile,\n modulePattern: RegExp,\n importedName: string,\n): ts.CallExpression[] {\n const locals = new Set(importedLocalNames(file, modulePattern, importedName));\n if (locals.size === 0) return [];\n const calls: ts.CallExpression[] = [];\n const visit = (node: ts.Node) => {\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && locals.has(node.expression.text)) {\n calls.push(node);\n }\n ts.forEachChild(node, visit);\n };\n visit(parseBrandlabSourceFile(file));\n return calls;\n}\n\nexport function lineOfNode(file: BrandlabSourceFile, node: ts.Node): number {\n const source = parseBrandlabSourceFile(file);\n return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;\n}\n\n/** The value of a named property in an object literal, or undefined. */\nexport function objectPropertyValue(\n node: ts.Node | undefined,\n name: string,\n): ts.Expression | undefined {\n if (!node || !ts.isObjectLiteralExpression(node)) return undefined;\n for (const property of node.properties) {\n if (!ts.isPropertyAssignment(property)) continue;\n const key = property.name;\n const keyText = ts.isIdentifier(key) || ts.isStringLiteralLike(key) ? key.text : null;\n if (keyText === name) return property.initializer;\n }\n return undefined;\n}\n\nexport function stringLiteralValue(node: ts.Node | undefined): string | null {\n return node && ts.isStringLiteralLike(node) ? node.text : null;\n}\n\nexport type BrandlabModuleResolver = (fromPath: string, specifier: string) => string | null;\n\n/** Resolve project-local specifiers (relative and tsconfig path aliases) to indexed files. */\nexport function createBrandlabModuleResolver(\n root: string,\n knownPaths: ReadonlySet<string>,\n): BrandlabModuleResolver {\n const aliases = readPathAliases(root);\n const candidates = (base: string) => [\n base,\n ...RESOLUTION_EXTENSIONS.map((extension) => `${base}${extension}`),\n ...RESOLUTION_EXTENSIONS.map((extension) => posix.join(base, `index${extension}`)),\n ...(base.endsWith(\".js\") ? RESOLUTION_EXTENSIONS.map((extension) => base.replace(/\\.js$/, extension)) : []),\n ];\n const find = (base: string) => {\n const normalised = posix.normalize(base).replace(/^\\.\\//, \"\");\n return candidates(normalised).find((candidate) => knownPaths.has(candidate)) ?? null;\n };\n return (fromPath, specifier) => {\n if (specifier.startsWith(\".\")) return find(posix.join(posix.dirname(fromPath), specifier));\n for (const alias of aliases) {\n if (alias.exact ? specifier !== alias.prefix : !specifier.startsWith(alias.prefix)) continue;\n const rest = alias.exact ? \"\" : specifier.slice(alias.prefix.length);\n for (const target of alias.targets) {\n const resolved = find(target.replace(\"*\", rest));\n if (resolved) return resolved;\n }\n }\n return null;\n };\n}\n\ninterface PathAlias {\n prefix: string;\n exact: boolean;\n targets: string[];\n}\n\nfunction readPathAliases(root: string): PathAlias[] {\n const fallback: PathAlias[] = [{ prefix: \"@/\", exact: false, targets: [\"src/*\"] }];\n const configPath = join(root, \"tsconfig.json\");\n if (!existsSync(configPath)) return fallback;\n const parsed = ts.readConfigFile(configPath, (path) => readFileSync(path, \"utf8\"));\n const options = parsed.config?.compilerOptions as { baseUrl?: string; paths?: Record<string, string[]> } | undefined;\n if (!options?.paths) return fallback;\n const baseUrl = posix.normalize((options.baseUrl ?? \".\").replaceAll(\"\\\\\", \"/\"));\n return Object.entries(options.paths)\n .filter(([, targets]) => Array.isArray(targets))\n .map(([pattern, targets]) => ({\n prefix: pattern.replace(/\\*$/, \"\"),\n exact: !pattern.endsWith(\"*\"),\n targets: targets.map((target) => posix.normalize(posix.join(baseUrl, target.replaceAll(\"\\\\\", \"/\")))),\n }));\n}\n\n/** Blank comments while preserving string contents, offsets and line breaks. */\nexport function stripCommentsPreservingLayout(source: string): string {\n const output = [...source];\n let state: \"code\" | \"single\" | \"double\" | \"template\" | \"line\" | \"block\" =\n \"code\";\n let escaped = false;\n\n for (let index = 0; index < output.length; index += 1) {\n const current = output[index];\n const next = output[index + 1];\n\n if (state === \"line\") {\n if (current === \"\\n\" || current === \"\\r\") {\n state = \"code\";\n } else {\n output[index] = \" \";\n }\n continue;\n }\n\n if (state === \"block\") {\n if (current === \"*\" && next === \"/\") {\n output[index] = \" \";\n output[index + 1] = \" \";\n index += 1;\n state = \"code\";\n } else if (current !== \"\\n\" && current !== \"\\r\") {\n output[index] = \" \";\n }\n continue;\n }\n\n if (state !== \"code\") {\n if (escaped) {\n escaped = false;\n } else if (current === \"\\\\\") {\n escaped = true;\n } else if (\n (state === \"single\" && current === \"'\") ||\n (state === \"double\" && current === '\"') ||\n (state === \"template\" && current === \"`\")\n ) {\n state = \"code\";\n }\n continue;\n }\n\n if (current === \"'\") {\n state = \"single\";\n } else if (current === '\"') {\n state = \"double\";\n } else if (current === \"`\") {\n state = \"template\";\n } else if (current === \"/\" && next === \"/\") {\n output[index] = \" \";\n output[index + 1] = \" \";\n index += 1;\n state = \"line\";\n } else if (current === \"/\" && next === \"*\") {\n output[index] = \" \";\n output[index + 1] = \" \";\n index += 1;\n state = \"block\";\n }\n }\n\n return output.join(\"\");\n}\n\nexport function lineAt(source: string, index: number): number {\n return source.slice(0, index).split(\"\\n\").length;\n}\n\nexport function isDirectory(path: string): boolean {\n try {\n return lstatSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\nexport function readOptionalText(path: string): string | null {\n return existsSync(path) ? readFileSync(path, \"utf8\") : null;\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BrandlabFinding } from "./project.js";
|
|
2
|
+
import { type BrandlabSourceFile, type BrandlabSourceIndex } from "./source-files.js";
|
|
3
|
+
import type { BrandlabRuleEvaluation } from "./verification-policy.js";
|
|
4
|
+
export interface StaticRuleContext {
|
|
5
|
+
root: string;
|
|
6
|
+
source: BrandlabSourceIndex;
|
|
7
|
+
/** brandlab.config.ts text, or null when it is missing. */
|
|
8
|
+
config: string | null;
|
|
9
|
+
findings: BrandlabFinding[];
|
|
10
|
+
evaluations: BrandlabRuleEvaluation[];
|
|
11
|
+
}
|
|
12
|
+
export declare function staticFinding(options: {
|
|
13
|
+
ruleId: string;
|
|
14
|
+
surface: string;
|
|
15
|
+
expected: string;
|
|
16
|
+
actual: string;
|
|
17
|
+
remediation: string;
|
|
18
|
+
path?: string;
|
|
19
|
+
line?: number;
|
|
20
|
+
severity?: BrandlabFinding["severity"];
|
|
21
|
+
}): BrandlabFinding;
|
|
22
|
+
export declare function recordEvaluation(evaluations: BrandlabRuleEvaluation[], surface: string, ...ruleIds: string[]): void;
|
|
23
|
+
/** The minimal static rules behind the checks that previously passed without evidence. */
|
|
24
|
+
export declare function inspectStaticRules(context: StaticRuleContext): void;
|
|
25
|
+
export interface BrandlabProjectCapabilities {
|
|
26
|
+
contactEnabled: boolean;
|
|
27
|
+
reviewsEnabled: boolean;
|
|
28
|
+
locationsDeclared: boolean;
|
|
29
|
+
sitemapMode: "single" | "split" | null;
|
|
30
|
+
}
|
|
31
|
+
/** Capabilities declared by brandlab.config.ts, the contact route and the location data. */
|
|
32
|
+
export declare function describeCapabilities(root: string, config: string | null): BrandlabProjectCapabilities;
|
|
33
|
+
/** Evaluate gitignore-style patterns (last match wins) for a root-level file. */
|
|
34
|
+
export declare function isIgnored(patterns: string, file: string): boolean;
|
|
35
|
+
export interface ContactContractRecipient {
|
|
36
|
+
path: string;
|
|
37
|
+
line: number;
|
|
38
|
+
/** The literal delivery.recipientEnv key, or null when it is not statically declared. */
|
|
39
|
+
recipientEnv: string | null;
|
|
40
|
+
}
|
|
41
|
+
/** Every defineContactContract call and the recipient environment key it declares. */
|
|
42
|
+
export declare function findContactContractRecipients(source: BrandlabSourceIndex): ContactContractRecipient[];
|
|
43
|
+
/** Names a module exports, including `export { local as POST }` forms. */
|
|
44
|
+
export declare function exportedNames(file: BrandlabSourceFile): Set<string>;
|
|
45
|
+
//# sourceMappingURL=static-rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-rules.d.ts","sourceRoot":"","sources":["../src/static-rules.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AASpD,OAAO,EAcL,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EAEzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAOvE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,mBAAmB,CAAC;IAC5B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,WAAW,EAAE,sBAAsB,EAAE,CAAC;CACvC;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;CACxC,GAAG,eAAe,CAalB;AAED,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,sBAAsB,EAAE,EACrC,OAAO,EAAE,MAAM,EACf,GAAG,OAAO,EAAE,MAAM,EAAE,QAGrB;AAED,0FAA0F;AAC1F,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,QAe5D;AAED,MAAM,WAAW,2BAA2B;IAC1C,cAAc,EAAE,OAAO,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,WAAW,EAAE,QAAQ,GAAG,OAAO,GAAG,IAAI,CAAC;CACxC;AAED,4FAA4F;AAC5F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,2BAA2B,CAgBrG;AAmqBD,iFAAiF;AACjF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAajE;AAID,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,yFAAyF;IACzF,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,sFAAsF;AACtF,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,mBAAmB,GAC1B,wBAAwB,EAAE,CAU5B;AA0BD,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,GAAG,GAAG,CAAC,MAAM,CAAC,CAgBnE"}
|