@controlfront/detect 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cfb.js +202 -0
- package/package.json +64 -0
- package/src/commands/baseline.js +198 -0
- package/src/commands/init.js +309 -0
- package/src/commands/login.js +71 -0
- package/src/commands/logout.js +44 -0
- package/src/commands/scan.js +1547 -0
- package/src/commands/snapshot.js +191 -0
- package/src/commands/sync.js +127 -0
- package/src/config/baseUrl.js +49 -0
- package/src/data/tailwind-core-spec.js +149 -0
- package/src/engine/runRules.js +210 -0
- package/src/lib/collectDeclaredTokensAuto.js +67 -0
- package/src/lib/collectTokenMatches.js +330 -0
- package/src/lib/collectTokenMatches.js.regex +252 -0
- package/src/lib/loadRules.js +73 -0
- package/src/rules/core/no-hardcoded-colors.js +28 -0
- package/src/rules/core/no-hardcoded-spacing.js +29 -0
- package/src/rules/core/no-inline-styles.js +28 -0
- package/src/utils/authorId.js +106 -0
- package/src/utils/buildAIContributions.js +224 -0
- package/src/utils/buildBlameData.js +388 -0
- package/src/utils/buildDeclaredCssVars.js +185 -0
- package/src/utils/buildDeclaredJson.js +214 -0
- package/src/utils/buildFileChanges.js +372 -0
- package/src/utils/buildRuntimeUsage.js +337 -0
- package/src/utils/detectDeclaredDrift.js +59 -0
- package/src/utils/extractImports.js +178 -0
- package/src/utils/fileExtensions.js +65 -0
- package/src/utils/generateInsights.js +332 -0
- package/src/utils/getAllFiles.js +63 -0
- package/src/utils/getCommitMetaData.js +102 -0
- package/src/utils/getLine.js +14 -0
- package/src/utils/resolveProjectForFolder/index.js +47 -0
- package/src/utils/twClassify.js +138 -0
package/bin/cfb.js
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
import https from "https";
|
|
6
|
+
import { runInit } from "../src/commands/init.js";
|
|
7
|
+
import { runLogin } from "../src/commands/login.js";
|
|
8
|
+
import { runLogout } from "../src/commands/logout.js";
|
|
9
|
+
import { runScan } from "../src/commands/scan.js";
|
|
10
|
+
import { runSync } from "../src/commands/sync.js";
|
|
11
|
+
dotenv.config();
|
|
12
|
+
|
|
13
|
+
import { createRequire } from "module";
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
const { version } = require("../package.json");
|
|
16
|
+
|
|
17
|
+
const program = new Command();
|
|
18
|
+
|
|
19
|
+
program.name("cf").description("ControlFront CLI").version(version);
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.command("scan")
|
|
23
|
+
.description("Run a local scan scan")
|
|
24
|
+
.option("--baseline", "Perform baseline scan instead of snapshot scan")
|
|
25
|
+
.option("--snapshot", "Perform snapshot scan instead of baseline scan")
|
|
26
|
+
.option("--sync", "Also sync the scan after scanning")
|
|
27
|
+
.option("--no-web-open", "Sync but do not open browser afterward")
|
|
28
|
+
.option(
|
|
29
|
+
"--changed-files <files>",
|
|
30
|
+
"Comma-separated list of changed files via GitHub commit/PR (optional)"
|
|
31
|
+
)
|
|
32
|
+
.option(
|
|
33
|
+
"--slug <slug>",
|
|
34
|
+
"Custom slug for CI scans (e.g. ci, ci-install, local)"
|
|
35
|
+
)
|
|
36
|
+
.option("--commit <sha>", "Run scan against a specific commit SHA")
|
|
37
|
+
.option(
|
|
38
|
+
"--make-baseline",
|
|
39
|
+
"Promote this scan's snapshot to become the active baseline"
|
|
40
|
+
)
|
|
41
|
+
.option(
|
|
42
|
+
"--last-90-days-all",
|
|
43
|
+
"Run a snapshot scan for every commit in the last 90 days"
|
|
44
|
+
)
|
|
45
|
+
.option(
|
|
46
|
+
"--last-180-days-all",
|
|
47
|
+
"Run a snapshot scan for every commit in the last 180 days"
|
|
48
|
+
)
|
|
49
|
+
.option(
|
|
50
|
+
"--range-sha <from,to>",
|
|
51
|
+
"Run a snapshot scan for every commit between two SHAs (inclusive), e.g. --range-sha abc123,def456"
|
|
52
|
+
)
|
|
53
|
+
.option(
|
|
54
|
+
"--range-date <from,to>",
|
|
55
|
+
"Run a snapshot scan for every commit between two dates (inclusive), e.g. --range-date 2025-01-01,2025-01-15"
|
|
56
|
+
)
|
|
57
|
+
.option(
|
|
58
|
+
"--last-commit",
|
|
59
|
+
"CI mode: snapshot HEAD (GITHUB_SHA) and sync immediately. Use in GitHub Actions with CF_INGEST_TOKEN, CF_PROJECT_ID, CF_WORKSPACE_ID."
|
|
60
|
+
)
|
|
61
|
+
.action((options) => {
|
|
62
|
+
const sync = options.sync;
|
|
63
|
+
const openWeb = options.webOpen !== false;
|
|
64
|
+
const changedFiles = options.changedFiles
|
|
65
|
+
? options.changedFiles.split(",").filter(Boolean)
|
|
66
|
+
: undefined;
|
|
67
|
+
const slug = options.slug;
|
|
68
|
+
|
|
69
|
+
const makeBaseline = options.makeBaseline === true;
|
|
70
|
+
const last90DaysAll = options.last90DaysAll === true;
|
|
71
|
+
const last180DaysAll = options.last180DaysAll === true;
|
|
72
|
+
const rangeSha = options.rangeSha;
|
|
73
|
+
const rangeDate = options.rangeDate;
|
|
74
|
+
const lastCommit = options.lastCommit === true;
|
|
75
|
+
|
|
76
|
+
const snapshot = options.snapshot === true;
|
|
77
|
+
|
|
78
|
+
// Legacy compatibility: --baseline implies snapshot + make-baseline
|
|
79
|
+
if (options.baseline === true) {
|
|
80
|
+
console.warn(
|
|
81
|
+
"⚠️ --baseline is deprecated. Use --snapshot --make-baseline instead."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --last-commit: CI shorthand — snapshot HEAD + sync, no backfill allowed
|
|
86
|
+
if (lastCommit) {
|
|
87
|
+
runScan({
|
|
88
|
+
lastCommit: true,
|
|
89
|
+
snapshot: true,
|
|
90
|
+
sync: true,
|
|
91
|
+
slug: slug || "ci",
|
|
92
|
+
openWeb: false,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!snapshot && !options.baseline && !last90DaysAll && !last180DaysAll && !rangeSha && !rangeDate) {
|
|
98
|
+
console.error("❌ You must specify --snapshot or --last-commit.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (rangeSha) {
|
|
103
|
+
const [fromSha, toSha] = rangeSha.split(",").map((s) => s.trim());
|
|
104
|
+
if (!fromSha || !toSha) {
|
|
105
|
+
console.error("❌ --range-sha requires two comma-separated SHAs, e.g. --range-sha abc123,def456");
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
runScan({
|
|
109
|
+
sync,
|
|
110
|
+
openWeb,
|
|
111
|
+
changedFiles,
|
|
112
|
+
slug,
|
|
113
|
+
snapshot: true,
|
|
114
|
+
makeBaseline,
|
|
115
|
+
rangeFromSha: fromSha,
|
|
116
|
+
rangeToSha: toSha,
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (rangeDate) {
|
|
122
|
+
const [fromDate, toDate] = rangeDate.split(",").map((s) => s.trim());
|
|
123
|
+
if (!fromDate || !toDate) {
|
|
124
|
+
console.error("❌ --range-date requires two comma-separated dates, e.g. --range-date 2025-01-01,2025-01-15");
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
runScan({
|
|
128
|
+
sync,
|
|
129
|
+
openWeb,
|
|
130
|
+
changedFiles,
|
|
131
|
+
slug,
|
|
132
|
+
snapshot: true,
|
|
133
|
+
makeBaseline,
|
|
134
|
+
rangeFromDate: fromDate,
|
|
135
|
+
rangeToDate: toDate,
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
runScan({
|
|
141
|
+
sync,
|
|
142
|
+
openWeb,
|
|
143
|
+
changedFiles,
|
|
144
|
+
slug,
|
|
145
|
+
snapshot: true,
|
|
146
|
+
makeBaseline,
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
program
|
|
151
|
+
.command("sync")
|
|
152
|
+
.description("Sync scan report to ControlFront web app")
|
|
153
|
+
.action(runSync);
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command("login")
|
|
157
|
+
.description("Log into your ControlFront account")
|
|
158
|
+
.option("--prod", "Login to production environment (default: development/localhost)")
|
|
159
|
+
.action((options) => runLogin(options));
|
|
160
|
+
|
|
161
|
+
program
|
|
162
|
+
.command("init")
|
|
163
|
+
.description("Initialize workspace and project for CLI use")
|
|
164
|
+
.action(runInit);
|
|
165
|
+
|
|
166
|
+
program
|
|
167
|
+
.command("logout")
|
|
168
|
+
.description("Log out of your ControlFront account")
|
|
169
|
+
.action(runLogout);
|
|
170
|
+
|
|
171
|
+
program.parse();
|
|
172
|
+
|
|
173
|
+
async function checkForUpdates() {
|
|
174
|
+
const currentVersion = version;
|
|
175
|
+
const url = "https://registry.npmjs.workspace/@controlfront/cli/latest";
|
|
176
|
+
|
|
177
|
+
https
|
|
178
|
+
.get(url, (res) => {
|
|
179
|
+
let data = "";
|
|
180
|
+
res.on("data", (chunk) => {
|
|
181
|
+
data += chunk;
|
|
182
|
+
});
|
|
183
|
+
res.on("end", () => {
|
|
184
|
+
try {
|
|
185
|
+
const json = JSON.parse(data);
|
|
186
|
+
const latest = json.version;
|
|
187
|
+
if (latest && latest !== currentVersion) {
|
|
188
|
+
console.log(
|
|
189
|
+
`⚠️ A newer version of cf is available: ${latest} (you have ${currentVersion}). Update with: npm install -g @controlfront/cli`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {
|
|
193
|
+
// ignore JSON parse errors
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
})
|
|
197
|
+
.on("error", () => {
|
|
198
|
+
// ignore errors
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
checkForUpdates();
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@controlfront/detect",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"description": "CLI tool for running ControlFront baseline scans and syncing with the ControlFront.io web app",
|
|
9
|
+
"bin": {
|
|
10
|
+
"cfdetect": "bin/cfb.js",
|
|
11
|
+
"controlfront-detect": "bin/cfb.js"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/",
|
|
16
|
+
"src/commands/",
|
|
17
|
+
"src/config/",
|
|
18
|
+
"src/engine/",
|
|
19
|
+
"src/baseline/",
|
|
20
|
+
"src/utils/",
|
|
21
|
+
"src/rules/core/",
|
|
22
|
+
"src/lib/",
|
|
23
|
+
"src/data/",
|
|
24
|
+
"!src/rules/dev/",
|
|
25
|
+
"!*.env"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start": "node ./bin/cfb.js",
|
|
29
|
+
"init": "node ./bin/cfb.js init",
|
|
30
|
+
"sync": "node ./bin/cfb.js sync",
|
|
31
|
+
"login": "node ./bin/cfb.js login",
|
|
32
|
+
"dev": "nodemon ./bin/cfb.js"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"controlfront",
|
|
36
|
+
"cli",
|
|
37
|
+
"ui-governance",
|
|
38
|
+
"ai-governance",
|
|
39
|
+
"frontend-governance"
|
|
40
|
+
],
|
|
41
|
+
"author": "ControlFront",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "git+https://github.com/controlfront/controlfront-cli-baseline.git"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@babel/parser": "^7.25.7",
|
|
49
|
+
"@babel/traverse": "^7.25.7",
|
|
50
|
+
"chalk": "^5.3.0",
|
|
51
|
+
"commander": "^14.0.0",
|
|
52
|
+
"dotenv": "^16.4.5",
|
|
53
|
+
"fast-glob": "^3.3.3",
|
|
54
|
+
"jsonwebtoken": "^9.0.2",
|
|
55
|
+
"micromatch": "^4.0.8",
|
|
56
|
+
"node-fetch": "^3.3.2",
|
|
57
|
+
"open": "^9.1.0",
|
|
58
|
+
"postcss": "^8.4.31",
|
|
59
|
+
"postcss-safe-parser": "^6.0.0",
|
|
60
|
+
"prompts": "^2.4.2",
|
|
61
|
+
"yaml": "^2.8.0"
|
|
62
|
+
},
|
|
63
|
+
"packageManager": "pnpm@10.5.2+sha512.da9dc28cd3ff40d0592188235ab25d3202add8a207afbedc682220e4a0029ffbff4562102b9e6e46b4e3f9e8bd53e6d05de48544b0c57d4b0179e22c76d1199b"
|
|
64
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// =============================================================
|
|
2
|
+
// ControlFront CLI — Baseline Mode
|
|
3
|
+
// Purpose: Establish structural baseline only (no drift compare)
|
|
4
|
+
// =============================================================
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import micromatch from "micromatch";
|
|
7
|
+
import path from "path";
|
|
8
|
+
import inferCatalog from "../baseline/components/inferCatalog.js";
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
|
|
11
|
+
// -------------------------------------------------------------
|
|
12
|
+
// Default Exclude Patterns
|
|
13
|
+
// -------------------------------------------------------------
|
|
14
|
+
const DEFAULT_EXCLUDE_PATTERNS = [
|
|
15
|
+
// build + vendor dirs
|
|
16
|
+
"**/node_modules/**",
|
|
17
|
+
"**/dist/**",
|
|
18
|
+
"**/build/**",
|
|
19
|
+
"**/.next/**",
|
|
20
|
+
"**/.turbo/**",
|
|
21
|
+
"**/coverage/**",
|
|
22
|
+
"**/.git/**",
|
|
23
|
+
"**/public/**",
|
|
24
|
+
"**/.cache/**",
|
|
25
|
+
"**/out/**",
|
|
26
|
+
|
|
27
|
+
// tests + stories
|
|
28
|
+
"**/*.test.*",
|
|
29
|
+
"**/*.spec.*",
|
|
30
|
+
"**/*.stories.*",
|
|
31
|
+
"**/*.d.ts",
|
|
32
|
+
"**/__mocks__/**",
|
|
33
|
+
"**/__tests__/**",
|
|
34
|
+
|
|
35
|
+
// CF artifacts (exclude everywhere in repo)
|
|
36
|
+
"**/baseline-structural.json",
|
|
37
|
+
"**/baseline-styles.json",
|
|
38
|
+
"**/baseline-components.json",
|
|
39
|
+
"**/baseline-outliers.json",
|
|
40
|
+
"**/baseline-structural-outliers.json",
|
|
41
|
+
"**/baseline-style-outliers.json",
|
|
42
|
+
"**/baseline-components-outliers.json",
|
|
43
|
+
"**/baseline.json",
|
|
44
|
+
"**/scan-report.json",
|
|
45
|
+
"**/cf-violations.json",
|
|
46
|
+
"**/insights.json",
|
|
47
|
+
"**/.cf/**",
|
|
48
|
+
|
|
49
|
+
// logs + env
|
|
50
|
+
"*.log",
|
|
51
|
+
".env",
|
|
52
|
+
".env.*",
|
|
53
|
+
|
|
54
|
+
// lockfiles
|
|
55
|
+
"package-lock.json",
|
|
56
|
+
"yarn.lock",
|
|
57
|
+
"pnpm-lock.yaml",
|
|
58
|
+
|
|
59
|
+
// big config JSONs (skip, not relevant to UI/CD)
|
|
60
|
+
"turbo.json",
|
|
61
|
+
"tsconfig*.json",
|
|
62
|
+
"package.json",
|
|
63
|
+
|
|
64
|
+
// misc config not relevant
|
|
65
|
+
"vite.config.*",
|
|
66
|
+
"next.config.*",
|
|
67
|
+
"tailwind.config.*",
|
|
68
|
+
"babel.config.*",
|
|
69
|
+
"postcss.config.*",
|
|
70
|
+
".eslintrc.*",
|
|
71
|
+
".prettierrc.*",
|
|
72
|
+
".stylelintrc.*",
|
|
73
|
+
".npmrc",
|
|
74
|
+
".nvmrc",
|
|
75
|
+
".DS_Store",
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
// -------------------------------------------------------------
|
|
79
|
+
// File Collection + Exclusions
|
|
80
|
+
// -------------------------------------------------------------
|
|
81
|
+
function getAllFiles(
|
|
82
|
+
dir,
|
|
83
|
+
exts = [
|
|
84
|
+
".js",
|
|
85
|
+
".ts",
|
|
86
|
+
".jsx",
|
|
87
|
+
".tsx",
|
|
88
|
+
".css",
|
|
89
|
+
".scss",
|
|
90
|
+
".sass",
|
|
91
|
+
".less",
|
|
92
|
+
".styl",
|
|
93
|
+
".vue",
|
|
94
|
+
".svelte",
|
|
95
|
+
".astro",
|
|
96
|
+
".html",
|
|
97
|
+
".htm",
|
|
98
|
+
".mdx",
|
|
99
|
+
".md",
|
|
100
|
+
".json",
|
|
101
|
+
".jsonc",
|
|
102
|
+
".yml",
|
|
103
|
+
".yaml",
|
|
104
|
+
".cjs",
|
|
105
|
+
".mjs",
|
|
106
|
+
],
|
|
107
|
+
files = [],
|
|
108
|
+
excludePatterns = DEFAULT_EXCLUDE_PATTERNS,
|
|
109
|
+
baseDir = process.cwd()
|
|
110
|
+
) {
|
|
111
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
112
|
+
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
const fullPath = path.join(dir, entry.name);
|
|
115
|
+
const relativePath = path.relative(baseDir, fullPath).replace(/\\/g, "/");
|
|
116
|
+
|
|
117
|
+
if (excludePatterns.length) {
|
|
118
|
+
const isExcluded = micromatch.isMatch(relativePath, excludePatterns, {
|
|
119
|
+
nocase: true,
|
|
120
|
+
});
|
|
121
|
+
// console.log(`Checking ${relativePath}, excluded: ${isExcluded}`);
|
|
122
|
+
if (isExcluded) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (entry.isDirectory()) {
|
|
128
|
+
getAllFiles(fullPath, exts, files, excludePatterns, baseDir);
|
|
129
|
+
} else if (exts.includes(path.extname(entry.name))) {
|
|
130
|
+
files.push(fullPath);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return files;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// =============================================================
|
|
138
|
+
// ControlFront CLI — Baseline Mode
|
|
139
|
+
// Purpose: Establish structural baseline only (no drift compare)
|
|
140
|
+
// =============================================================
|
|
141
|
+
|
|
142
|
+
export async function runBaseline({ files } = {}) {
|
|
143
|
+
if (!files) {
|
|
144
|
+
files = getAllFiles(process.cwd());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const cssFiles = files.filter((f) => /\.(css|scss|sass|less|styl)$/.test(f));
|
|
148
|
+
const codeFiles = files.filter((f) => /\.(js|jsx|ts|tsx)$/.test(f));
|
|
149
|
+
const jsonFiles = files.filter((f) => /\.json$/.test(f));
|
|
150
|
+
|
|
151
|
+
const { buildStructuralBaseline } = await import(
|
|
152
|
+
"../baseline/structural/buildStructuralBaseline.js"
|
|
153
|
+
);
|
|
154
|
+
const { buildComponentsBaseline } = await import(
|
|
155
|
+
"../baseline/components/buildComponentsBaseline.js"
|
|
156
|
+
);
|
|
157
|
+
const { buildStylesBaseline } = await import(
|
|
158
|
+
"../baseline/styles/buildStylesBaseline.js"
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
const catalog = await inferCatalog(codeFiles);
|
|
162
|
+
|
|
163
|
+
const structuralBaseline = await buildStructuralBaseline({ files });
|
|
164
|
+
const componentsBaseline = await buildComponentsBaseline({ files: codeFiles, catalog });
|
|
165
|
+
const stylesBaseline = await buildStylesBaseline({ cssFiles, codeFiles, jsonFiles });
|
|
166
|
+
|
|
167
|
+
let commit_sha = null;
|
|
168
|
+
let commit_author = null;
|
|
169
|
+
let commit_timestamp = null;
|
|
170
|
+
let commit_branch = null;
|
|
171
|
+
let commit_message = null;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
commit_sha = execSync("git rev-parse HEAD").toString().trim();
|
|
175
|
+
commit_author = execSync("git log -1 --pretty=format:%an").toString().trim();
|
|
176
|
+
commit_timestamp = execSync("git log -1 --pretty=format:%cI").toString().trim();
|
|
177
|
+
commit_branch = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
|
|
178
|
+
commit_message = execSync("git log -1 --pretty=format:%s").toString().trim();
|
|
179
|
+
} catch {}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
summary: {
|
|
183
|
+
baseline: {
|
|
184
|
+
metrics: {
|
|
185
|
+
commit_sha,
|
|
186
|
+
commit_author,
|
|
187
|
+
commit_timestamp,
|
|
188
|
+
commit_branch,
|
|
189
|
+
commit_message,
|
|
190
|
+
},
|
|
191
|
+
details: {}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
structural: structuralBaseline,
|
|
195
|
+
components: componentsBaseline,
|
|
196
|
+
styles: stylesBaseline,
|
|
197
|
+
};
|
|
198
|
+
}
|