@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 CHANGED
@@ -1,6 +1,10 @@
1
1
  # @justinmoto/frontend-guardian-core
2
2
 
3
- Scan engine for **Frontend Guardian**. Use this package only if you want to run the scanner from your own Node code (programmatic use).
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
 
@@ -1 +1 @@
1
- {"version":3,"file":"scanEngine.d.ts","sourceRoot":"","sources":["../src/scanEngine.ts"],"names":[],"mappings":"AASA,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;AA2W/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"}
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"}
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@justinmoto/frontend-guardian-core",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Scan engine for Frontend Guardian. To run scans from the CLI use: npx frontend-guardian .",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",