@justinmoto/frontend-guardian-core 0.1.13 → 0.1.15
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/README.md +5 -1
- package/dist/scanEngine.d.ts.map +1 -1
- package/dist/scanEngine.js +64 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @justinmoto/frontend-guardian-core
|
|
2
2
|
|
|
3
|
-
Scan engine for **Frontend Guardian**.
|
|
3
|
+
Scan engine for **Frontend Guardian**.
|
|
4
|
+
|
|
5
|
+
**What is Frontend Guardian?** It helps frontend teams keep UIs consistent. After you ship, spacing, borders, colors, and components often drift. This tool scans your code (Tailwind, JSX/TSX) and flags inconsistent spacing, arbitrary colors, mixed border-radius, duplicate components, and similar issues so you can fix them before they pile up.
|
|
6
|
+
|
|
7
|
+
Use this package only if you want to run the scanner from your own Node code (programmatic use).
|
|
4
8
|
|
|
5
9
|
**Most users:** run the CLI instead (no install):
|
|
6
10
|
|
package/dist/scanEngine.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanEngine.d.ts","sourceRoot":"","sources":["../src/scanEngine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scanEngine.d.ts","sourceRoot":"","sources":["../src/scanEngine.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AACF,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAClF,MAAM,MAAM,UAAU,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;IAAC,UAAU,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AAsZ/G,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAa/E;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,UAAU,CAG7E"}
|
package/dist/scanEngine.js
CHANGED
|
@@ -3,8 +3,20 @@ import _traverse from "@babel/traverse";
|
|
|
3
3
|
const traverse = (typeof _traverse === "function" ? _traverse : _traverse.default);
|
|
4
4
|
import md5 from "md5";
|
|
5
5
|
import JSZip from "jszip";
|
|
6
|
-
const EXT = [".js", ".jsx", ".ts", ".tsx"];
|
|
6
|
+
const EXT = [".js", ".jsx", ".ts", ".tsx", ".css"];
|
|
7
7
|
const IGNORE_PATH_PARTS = ["node_modules", ".next", "dist", "build", ".git"];
|
|
8
|
+
function extractApplyClassesFromCss(code) {
|
|
9
|
+
const out = [];
|
|
10
|
+
const regex = /@apply\s+([^;]+);/g;
|
|
11
|
+
let match;
|
|
12
|
+
while ((match = regex.exec(code)) !== null) {
|
|
13
|
+
const lineNum = code.slice(0, match.index).split("\n").length;
|
|
14
|
+
const value = match[1].replace(/\s+/g, " ").trim();
|
|
15
|
+
if (value)
|
|
16
|
+
out.push({ value, line: lineNum });
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
8
20
|
function isCodeFile(path) {
|
|
9
21
|
return EXT.some((e) => path.toLowerCase().endsWith(e));
|
|
10
22
|
}
|
|
@@ -167,6 +179,57 @@ function runAnalysis(fileContents) {
|
|
|
167
179
|
return spacingByContext.get(context);
|
|
168
180
|
}
|
|
169
181
|
for (const { path: filePath, code } of sourceOnly) {
|
|
182
|
+
const isCss = filePath.toLowerCase().endsWith(".css");
|
|
183
|
+
if (isCss) {
|
|
184
|
+
const classEntries = extractApplyClassesFromCss(code);
|
|
185
|
+
for (const { value: cn, line } of classEntries) {
|
|
186
|
+
const context = isCardLike(cn) ? "card" : "default";
|
|
187
|
+
const spacingData = getSpacingMap(context);
|
|
188
|
+
const parts = cn.split(/\s+/).filter(Boolean);
|
|
189
|
+
for (const p of parts) {
|
|
190
|
+
if (/^p[xy]?-[a-z0-9]+$/.test(p) || /^m[xy]?-[a-z0-9]+$/.test(p)) {
|
|
191
|
+
const key = p.replace(/[0-9]+$/, "N");
|
|
192
|
+
if (!spacingData.has(key))
|
|
193
|
+
spacingData.set(key, { values: new Set(), locations: [] });
|
|
194
|
+
const entry = spacingData.get(key);
|
|
195
|
+
entry.values.add(p);
|
|
196
|
+
if (entry.locations.length < MAX_LOCATIONS)
|
|
197
|
+
entry.locations.push({ file: filePath, line });
|
|
198
|
+
}
|
|
199
|
+
if (/^rounded[a-z0-9-]*$/.test(p)) {
|
|
200
|
+
if (!radiusByFile.has(filePath))
|
|
201
|
+
radiusByFile.set(filePath, { values: new Set(), line });
|
|
202
|
+
radiusByFile.get(filePath).values.add(p);
|
|
203
|
+
}
|
|
204
|
+
if (/^(bg|text|border)-(.+)$/.test(p)) {
|
|
205
|
+
if (!colorClassesByFile.has(filePath))
|
|
206
|
+
colorClassesByFile.set(filePath, new Set());
|
|
207
|
+
colorClassesByFile.get(filePath).add(p);
|
|
208
|
+
const colorPart = p.replace(/^(bg|text|border)-/, "");
|
|
209
|
+
if (/\[#|\[rgb|\[hsl/.test(colorPart)) {
|
|
210
|
+
arbitraryColorByFile.set(filePath, line);
|
|
211
|
+
const hexMatch = colorPart.match(/\[#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})\]/);
|
|
212
|
+
if (hexMatch) {
|
|
213
|
+
let hex = hexMatch[1].toLowerCase();
|
|
214
|
+
if (hex.length === 3)
|
|
215
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
216
|
+
const key = "#" + hex;
|
|
217
|
+
if (!hexUsedInPlaces.has(key))
|
|
218
|
+
hexUsedInPlaces.set(key, []);
|
|
219
|
+
const locs = hexUsedInPlaces.get(key);
|
|
220
|
+
if (locs.length < MAX_LOCATIONS)
|
|
221
|
+
locs.push({ file: filePath, line });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (PX_ARBITRARY.test(p) && !BORDER_PX_ONLY.test(p)) {
|
|
226
|
+
if (pixelUsageLocations.length < MAX_LOCATIONS)
|
|
227
|
+
pixelUsageLocations.push({ file: filePath, line });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
170
233
|
try {
|
|
171
234
|
const ast = parser.parse(code, { sourceType: "module", plugins: ["jsx", "typescript"], attachComment: false });
|
|
172
235
|
const unusedList = getUnusedImportsWithLine(ast);
|
package/package.json
CHANGED