@empline/preflight 1.1.14 → 1.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.
Files changed (49) hide show
  1. package/dist/checks/auth/session-provider-wrapper.d.ts +47 -0
  2. package/dist/checks/auth/session-provider-wrapper.d.ts.map +1 -0
  3. package/dist/checks/auth/session-provider-wrapper.js +286 -0
  4. package/dist/checks/auth/session-provider-wrapper.js.map +1 -0
  5. package/dist/checks/database/prisma-upsert-safety.d.ts +39 -0
  6. package/dist/checks/database/prisma-upsert-safety.d.ts.map +1 -0
  7. package/dist/checks/database/prisma-upsert-safety.js +220 -0
  8. package/dist/checks/database/prisma-upsert-safety.js.map +1 -0
  9. package/dist/checks/dependencies/dependency-health-monitor.d.ts +49 -0
  10. package/dist/checks/dependencies/dependency-health-monitor.d.ts.map +1 -0
  11. package/dist/checks/dependencies/dependency-health-monitor.js +323 -0
  12. package/dist/checks/dependencies/dependency-health-monitor.js.map +1 -0
  13. package/dist/checks/file-hygiene-validation.d.ts +31 -0
  14. package/dist/checks/file-hygiene-validation.d.ts.map +1 -0
  15. package/dist/checks/file-hygiene-validation.js +934 -0
  16. package/dist/checks/file-hygiene-validation.js.map +1 -0
  17. package/dist/checks/organization/file-cleanup-validation.d.ts +22 -0
  18. package/dist/checks/organization/file-cleanup-validation.d.ts.map +1 -0
  19. package/dist/checks/organization/file-cleanup-validation.js +1121 -0
  20. package/dist/checks/organization/file-cleanup-validation.js.map +1 -0
  21. package/dist/checks/runtime/tailwind-runtime-check.d.ts +36 -0
  22. package/dist/checks/runtime/tailwind-runtime-check.d.ts.map +1 -0
  23. package/dist/checks/runtime/tailwind-runtime-check.js +264 -0
  24. package/dist/checks/runtime/tailwind-runtime-check.js.map +1 -0
  25. package/dist/checks/shipping-integration-validation.d.ts +28 -0
  26. package/dist/checks/shipping-integration-validation.d.ts.map +1 -0
  27. package/dist/checks/shipping-integration-validation.js +409 -0
  28. package/dist/checks/shipping-integration-validation.js.map +1 -0
  29. package/dist/checks/system/layout-constants-sync.d.ts +36 -0
  30. package/dist/checks/system/layout-constants-sync.d.ts.map +1 -0
  31. package/dist/checks/system/layout-constants-sync.js +642 -0
  32. package/dist/checks/system/layout-constants-sync.js.map +1 -0
  33. package/dist/checks/system/preflight-circular-dependency-detector.d.ts +26 -0
  34. package/dist/checks/system/preflight-circular-dependency-detector.d.ts.map +1 -0
  35. package/dist/checks/system/preflight-circular-dependency-detector.js +310 -0
  36. package/dist/checks/system/preflight-circular-dependency-detector.js.map +1 -0
  37. package/dist/checks/system/preflight-execution-benchmarks.d.ts +24 -0
  38. package/dist/checks/system/preflight-execution-benchmarks.d.ts.map +1 -0
  39. package/dist/checks/system/preflight-execution-benchmarks.js +282 -0
  40. package/dist/checks/system/preflight-execution-benchmarks.js.map +1 -0
  41. package/dist/checks/system/preflight-tag-taxonomy-validator.d.ts +27 -0
  42. package/dist/checks/system/preflight-tag-taxonomy-validator.d.ts.map +1 -0
  43. package/dist/checks/system/preflight-tag-taxonomy-validator.js +361 -0
  44. package/dist/checks/system/preflight-tag-taxonomy-validator.js.map +1 -0
  45. package/dist/utils/console-chars.d.ts +16 -0
  46. package/dist/utils/console-chars.d.ts.map +1 -1
  47. package/dist/utils/console-chars.js +10 -0
  48. package/dist/utils/console-chars.js.map +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,642 @@
1
+ #!/usr/bin/env tsx
2
+ "use strict";
3
+ /**
4
+ * Layout Constants Sync Preflight - IRON-CLAD ENFORCEMENT
5
+ *
6
+ * Ensures consistency between:
7
+ * 1. Shared layout constants (single source of truth)
8
+ * 2. Actual layout component implementations
9
+ * 3. Preflights that reference layout components
10
+ * 4. Identifies opportunities for consolidation (shown as warnings)
11
+ *
12
+ * This prevents drift where constants say one thing but implementations do another.
13
+ *
14
+ * Usage:
15
+ * pnpm preflight:layout-constants-sync # All checks including opportunities
16
+ * pnpm preflight:layout-constants-sync --verbose # Show all details
17
+ *
18
+ * Checks performed:
19
+ * 1. Layout components implement expected gap values (BLOCKING)
20
+ * 2. Preflights import from shared constants (not hardcoded)
21
+ * 3. All registered components exist (BLOCKING)
22
+ * 4. Unregistered gap layouts are flagged
23
+ * 5. Hardcoded spacing that could use constants (WARNING)
24
+ * 6. Components that could benefit from shared constants (WARNING)
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || (function () {
43
+ var ownKeys = function(o) {
44
+ ownKeys = Object.getOwnPropertyNames || function (o) {
45
+ var ar = [];
46
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
47
+ return ar;
48
+ };
49
+ return ownKeys(o);
50
+ };
51
+ return function (mod) {
52
+ if (mod && mod.__esModule) return mod;
53
+ var result = {};
54
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
55
+ __setModuleDefault(result, mod);
56
+ return result;
57
+ };
58
+ })();
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.requires = exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
61
+ exports.run = run;
62
+ const fs = __importStar(require("fs"));
63
+ const console_chars_1 = require("../../utils/console-chars");
64
+ const file_cache_1 = require("../../shared/file-cache");
65
+ const glob_patterns_1 = require("../../shared/glob-patterns");
66
+ const universal_progress_reporter_1 = require("./universal-progress-reporter");
67
+ // Check metadata
68
+ exports.id = "system/layout-constants-sync";
69
+ exports.name = "Layout Constants Sync";
70
+ exports.category = "system";
71
+ exports.blocking = true;
72
+ exports.description = "Layout Constants Sync Preflight - IRON-CLAD ENFORCEMENT";
73
+ exports.tags = ["system"];
74
+ exports.requires = ["trading-card-system"];
75
+ // Dynamic imports for project-specific modules
76
+ let GAP_LAYOUT_COMPONENTS = [];
77
+ let LAYOUT_GAP_SPACING = 6;
78
+ let LAYOUT_PADDING_TOP_SPACING = 4;
79
+ let TOAST_CONTAINER_GAP_SPACING = 3;
80
+ async function loadProjectDependencies() {
81
+ try {
82
+ const layoutConstantsPath = "../../shared/layout-constants";
83
+ const mod = await Promise.resolve(`${layoutConstantsPath}`).then(s => __importStar(require(s)));
84
+ GAP_LAYOUT_COMPONENTS = mod.GAP_LAYOUT_COMPONENTS;
85
+ LAYOUT_GAP_SPACING = mod.LAYOUT_GAP_SPACING;
86
+ LAYOUT_PADDING_TOP_SPACING = mod.LAYOUT_PADDING_TOP_SPACING;
87
+ TOAST_CONTAINER_GAP_SPACING = mod.TOAST_CONTAINER_GAP_SPACING;
88
+ return true;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ }
94
+ // CACHED FILE LISTS - Scan once, use everywhere
95
+ let _cachedPreflightFiles = null;
96
+ async function getPreflightFiles() {
97
+ if (!_cachedPreflightFiles) {
98
+ _cachedPreflightFiles = await file_cache_1.fileCache.getFiles("scripts/active/preflights/**/*.ts", {
99
+ ignore: glob_patterns_1.STANDARD_EXCLUDES,
100
+ });
101
+ }
102
+ return _cachedPreflightFiles;
103
+ }
104
+ let _cachedAppComponentsTsxFiles = null;
105
+ async function getAppComponentsTsxFiles() {
106
+ if (!_cachedAppComponentsTsxFiles) {
107
+ _cachedAppComponentsTsxFiles = await file_cache_1.fileCache.getAppAndComponentsTSX();
108
+ }
109
+ return _cachedAppComponentsTsxFiles;
110
+ }
111
+ let _cachedLayoutFiles = null;
112
+ async function getLayoutComponentFiles() {
113
+ if (!_cachedLayoutFiles) {
114
+ _cachedLayoutFiles = await file_cache_1.fileCache.getFiles("components/**/*Layout*.tsx", {
115
+ ignore: glob_patterns_1.STANDARD_EXCLUDES,
116
+ });
117
+ }
118
+ return _cachedLayoutFiles;
119
+ }
120
+ const EXCLUDED = [...glob_patterns_1.STANDARD_EXCLUDES];
121
+ /**
122
+ * Check 1: Verify layout components actually use the expected gap value
123
+ *
124
+ * Note: Some layout components are thin wrappers around DashboardPageLayout.
125
+ * These wrappers don't define gap themselves - they delegate to DashboardPageLayout.
126
+ * We only check components that actually define their own gap.
127
+ */
128
+ async function checkLayoutComponentsImplementGap() {
129
+ const issues = [];
130
+ // Map component names to their file paths
131
+ const componentFiles = {
132
+ DashboardPageLayout: "components/shared/DashboardPageLayout.tsx",
133
+ AdminPageLayout: "components/shared/AdminPageLayout.tsx",
134
+ SellerPageLayout: "components/shared/SellerPageLayout.tsx",
135
+ AccountPageLayout: "components/shared/AccountPageLayout.tsx",
136
+ };
137
+ // Components that are thin wrappers - they use DashboardPageLayout internally
138
+ // These don't need their own gap definition
139
+ const wrapperComponents = new Set([
140
+ "AdminPageLayout",
141
+ "SellerPageLayout",
142
+ "AccountPageLayout",
143
+ "TemplatesLayout",
144
+ ]);
145
+ for (const component of GAP_LAYOUT_COMPONENTS) {
146
+ const filePath = componentFiles[component];
147
+ if (!filePath || !fs.existsSync(filePath)) {
148
+ // Skip wrapper components like TemplatesLayout - they use the base layouts
149
+ continue;
150
+ }
151
+ const content = fs.readFileSync(filePath, "utf8");
152
+ // Check if this is a wrapper that delegates to DashboardPageLayout
153
+ const isDelegatingWrapper = wrapperComponents.has(component) && content.includes("DashboardPageLayout");
154
+ if (isDelegatingWrapper) {
155
+ // Verify it actually uses DashboardPageLayout
156
+ if (!/<DashboardPageLayout/.test(content)) {
157
+ issues.push({
158
+ file: filePath,
159
+ type: "wrapper-not-delegating",
160
+ severity: "error",
161
+ message: `${component} is expected to wrap DashboardPageLayout but doesn't`,
162
+ suggestion: `Use <DashboardPageLayout variant="..."> inside ${component}`,
163
+ });
164
+ }
165
+ // Skip gap/padding checks for wrappers - DashboardPageLayout handles it
166
+ continue;
167
+ }
168
+ // Check for gap: var(--spacing-X) where X should match LAYOUT_GAP_SPACING
169
+ const gapMatches = content.matchAll(/gap:\s*["']var\(--spacing-(\d+)\)["']/g);
170
+ let foundCorrectGap = false;
171
+ for (const match of gapMatches) {
172
+ const gapValue = parseInt(match[1]);
173
+ if (gapValue === LAYOUT_GAP_SPACING) {
174
+ foundCorrectGap = true;
175
+ }
176
+ else {
177
+ const lineNumber = content.substring(0, match.index).split("\n").length;
178
+ issues.push({
179
+ file: filePath,
180
+ line: lineNumber,
181
+ type: "layout-wrong-gap",
182
+ severity: "error",
183
+ message: `${component} uses gap: spacing-${gapValue} but shared constant expects spacing-${LAYOUT_GAP_SPACING}`,
184
+ suggestion: `Change to gap: "var(--spacing-${LAYOUT_GAP_SPACING})" or update LAYOUT_GAP_SPACING in layout-constants.ts`,
185
+ });
186
+ }
187
+ }
188
+ // Check for paddingTop: var(--spacing-X) where X should match LAYOUT_PADDING_TOP_SPACING
189
+ const paddingMatches = content.matchAll(/paddingTop:\s*["']var\(--spacing-(\d+)\)["']/g);
190
+ for (const match of paddingMatches) {
191
+ const paddingValue = parseInt(match[1]);
192
+ if (paddingValue !== LAYOUT_PADDING_TOP_SPACING) {
193
+ const lineNumber = content.substring(0, match.index).split("\n").length;
194
+ issues.push({
195
+ file: filePath,
196
+ line: lineNumber,
197
+ type: "layout-wrong-padding-top",
198
+ severity: "error",
199
+ message: `${component} uses paddingTop: spacing-${paddingValue} but shared constant expects spacing-${LAYOUT_PADDING_TOP_SPACING}`,
200
+ suggestion: `Change to paddingTop: "var(--spacing-${LAYOUT_PADDING_TOP_SPACING})" or update LAYOUT_PADDING_TOP_SPACING in layout-constants.ts`,
201
+ });
202
+ }
203
+ }
204
+ // Warn if no gap found at all (only for non-wrapper components)
205
+ if (!foundCorrectGap && !content.includes(`gap:`)) {
206
+ issues.push({
207
+ file: filePath,
208
+ type: "layout-missing-gap",
209
+ severity: "warning",
210
+ message: `${component} is in GAP_LAYOUT_COMPONENTS but doesn't appear to use gap spacing`,
211
+ suggestion: `Add gap: "var(--spacing-${LAYOUT_GAP_SPACING})" to the content area style`,
212
+ });
213
+ }
214
+ }
215
+ return issues;
216
+ }
217
+ /**
218
+ * Check 2: Verify preflights import from shared constants (not hardcoded)
219
+ */
220
+ async function checkPreflightsUseSharedConstants() {
221
+ const issues = [];
222
+ // Find all preflight files that might reference layout components
223
+ const preflightFiles = await getPreflightFiles();
224
+ // Skip the shared constants file itself
225
+ const sharedConstantsPath = "scripts/active/preflights/shared/layout-constants.ts";
226
+ for (const file of preflightFiles) {
227
+ if (file.replace(/\\/g, "/").includes("shared/layout-constants"))
228
+ continue;
229
+ const content = fs.readFileSync(file, "utf8");
230
+ const lines = content.split("\n");
231
+ // Check for hardcoded layout component arrays
232
+ const hardcodedPatterns = [
233
+ /const\s+\w*[Ll]ayout[Cc]omponents?\w*\s*=\s*\[/,
234
+ /const\s+GAP_LAYOUTS\s*=\s*\[(?!\.\.\.GAP_LAYOUT_COMPONENTS)/,
235
+ /const\s+LAYOUT_COMPONENTS_WITH_GAP\s*=\s*\[(?!\.\.\.GAP_LAYOUT_COMPONENTS)/,
236
+ ];
237
+ for (let i = 0; i < lines.length; i++) {
238
+ const line = lines[i];
239
+ for (const pattern of hardcodedPatterns) {
240
+ if (pattern.test(line)) {
241
+ // Check if this file imports from shared constants
242
+ const hasSharedImport = content.includes("from '../shared/layout-constants'") ||
243
+ content.includes("from './shared/layout-constants'") ||
244
+ content.includes("from '../../shared/layout-constants'");
245
+ if (!hasSharedImport) {
246
+ issues.push({
247
+ file,
248
+ line: i + 1,
249
+ type: "hardcoded-layout-list",
250
+ severity: "warning",
251
+ message: "Preflight has hardcoded layout component list instead of using shared constants",
252
+ suggestion: "Import GAP_LAYOUT_COMPONENTS from '../shared/layout-constants' and use it",
253
+ });
254
+ }
255
+ }
256
+ }
257
+ }
258
+ // Check for hardcoded spacing values that should use constants
259
+ const spacingPatterns = [
260
+ { pattern: /DASHBOARD_LAYOUT_PADDING_TOP:\s*(\d+)/, name: "DASHBOARD_LAYOUT_PADDING_TOP" },
261
+ { pattern: /LAYOUT_GAP_SPACING\s*=\s*(\d+)/, name: "LAYOUT_GAP_SPACING" },
262
+ ];
263
+ for (const { pattern, name } of spacingPatterns) {
264
+ const match = content.match(pattern);
265
+ if (match && !file.includes("layout-constants")) {
266
+ const hasSharedImport = content.includes("from '../shared/layout-constants'") ||
267
+ content.includes("from './shared/layout-constants'");
268
+ if (!hasSharedImport) {
269
+ issues.push({
270
+ file,
271
+ type: "hardcoded-spacing-value",
272
+ severity: "warning",
273
+ message: `Preflight defines ${name} locally instead of importing from shared constants`,
274
+ suggestion: `Import ${name} from layout-constants.ts`,
275
+ });
276
+ }
277
+ }
278
+ }
279
+ }
280
+ return issues;
281
+ }
282
+ /**
283
+ * Check 3: Verify all layout components in GAP_LAYOUT_COMPONENTS actually exist
284
+ */
285
+ async function checkLayoutComponentsExist() {
286
+ const issues = [];
287
+ // Find all component files
288
+ const componentFiles = await getAppComponentsTsxFiles();
289
+ const componentNames = new Set();
290
+ for (const file of componentFiles) {
291
+ const content = fs.readFileSync(file, "utf8");
292
+ // Extract exported component names
293
+ const exportMatches = content.matchAll(/export\s+(?:default\s+)?function\s+(\w+)/g);
294
+ for (const match of exportMatches) {
295
+ componentNames.add(match[1]);
296
+ }
297
+ // Also check for const exports
298
+ const constExportMatches = content.matchAll(/export\s+(?:const|let)\s+(\w+)/g);
299
+ for (const match of constExportMatches) {
300
+ componentNames.add(match[1]);
301
+ }
302
+ }
303
+ // Check each component in GAP_LAYOUT_COMPONENTS exists
304
+ for (const component of GAP_LAYOUT_COMPONENTS) {
305
+ if (!componentNames.has(component)) {
306
+ issues.push({
307
+ file: "scripts/active/preflights/shared/layout-constants.ts",
308
+ type: "missing-layout-component",
309
+ severity: "error",
310
+ message: `GAP_LAYOUT_COMPONENTS includes "${component}" but this component doesn't exist`,
311
+ suggestion: `Remove "${component}" from GAP_LAYOUT_COMPONENTS or create the component`,
312
+ });
313
+ }
314
+ }
315
+ return issues;
316
+ }
317
+ /**
318
+ * Check 4: Find layout components that use gap but aren't in GAP_LAYOUT_COMPONENTS
319
+ */
320
+ async function checkUnregisteredGapLayouts() {
321
+ const issues = [];
322
+ // Find layout component files
323
+ const layoutFiles = await getLayoutComponentFiles();
324
+ for (const file of layoutFiles) {
325
+ const content = fs.readFileSync(file, "utf8");
326
+ // Check if this file defines a gap-based layout
327
+ const hasGap = /gap:\s*["']var\(--spacing-\d+\)["']/.test(content);
328
+ const hasFlexColumn = /flexDirection:\s*["']column["']/.test(content) || /flex-col/.test(content);
329
+ if (hasGap && hasFlexColumn) {
330
+ // Extract component name from file
331
+ const componentMatch = content.match(/export\s+(?:default\s+)?function\s+(\w*Layout\w*)/);
332
+ if (componentMatch) {
333
+ const componentName = componentMatch[1];
334
+ // Check if it's in GAP_LAYOUT_COMPONENTS
335
+ if (!GAP_LAYOUT_COMPONENTS.includes(componentName)) {
336
+ issues.push({
337
+ file,
338
+ type: "unregistered-gap-layout",
339
+ severity: "warning",
340
+ message: `${componentName} uses gap spacing but isn't in GAP_LAYOUT_COMPONENTS`,
341
+ suggestion: `Add "${componentName}" to GAP_LAYOUT_COMPONENTS in layout-constants.ts if children shouldn't have margin`,
342
+ });
343
+ }
344
+ }
345
+ }
346
+ }
347
+ return issues;
348
+ }
349
+ /**
350
+ * Check 4b: Verify toast/notification containers have proper gap spacing
351
+ *
352
+ * Toast containers should use gap: var(--spacing-3) to ensure consistent
353
+ * spacing between stacked notifications in the upper right corner.
354
+ */
355
+ async function checkToastContainerGaps() {
356
+ const issues = [];
357
+ // Files that contain toast container implementations
358
+ const toastContainerFiles = [
359
+ "components/shared/status/StatusProvider.tsx",
360
+ "components/ToastProvider.tsx",
361
+ "components/shared/notifications/Toast.tsx",
362
+ "components/shared/notifications/NotificationProvider.tsx",
363
+ "packages/ui/src/components/feedback/Toast.tsx",
364
+ ];
365
+ for (const filePath of toastContainerFiles) {
366
+ if (!fs.existsSync(filePath))
367
+ continue;
368
+ const content = fs.readFileSync(filePath, "utf8");
369
+ const lines = content.split("\n");
370
+ // Check for flex-col containers that render multiple toasts/notifications
371
+ // These should have gap spacing
372
+ const flexColContainerPatterns = [
373
+ /flexDirection:\s*["']column["']/,
374
+ /flex-col/,
375
+ /flex\s+flex-col/,
376
+ ];
377
+ const hasFlexColumn = flexColContainerPatterns.some((p) => p.test(content));
378
+ // Check if file renders multiple toast items (map over toasts/notifications)
379
+ const rendersMultipleToasts = /\.map\s*\(\s*\(?\s*(?:toast|notification|item)/i.test(content) ||
380
+ /toasts\.map|notifications\.map/i.test(content);
381
+ if (hasFlexColumn && rendersMultipleToasts) {
382
+ // Check for gap in the container
383
+ const hasGapStyle = /gap:\s*["']var\(--spacing-(\d+)\)["']/.test(content);
384
+ const hasGapClass = /gap-(\d+)/.test(content);
385
+ if (!hasGapStyle && !hasGapClass) {
386
+ issues.push({
387
+ file: filePath,
388
+ type: "toast-container-missing-gap",
389
+ severity: "error",
390
+ message: `Toast container missing gap spacing between stacked items`,
391
+ suggestion: `Add gap: "var(--spacing-${TOAST_CONTAINER_GAP_SPACING})" or className="gap-${TOAST_CONTAINER_GAP_SPACING}" to the flex-col container`,
392
+ });
393
+ }
394
+ else {
395
+ // Verify correct gap value
396
+ const styleGapMatch = content.match(/gap:\s*["']var\(--spacing-(\d+)\)["']/);
397
+ const classGapMatch = content.match(/gap-(\d+)/);
398
+ const gapValue = styleGapMatch
399
+ ? parseInt(styleGapMatch[1])
400
+ : classGapMatch
401
+ ? parseInt(classGapMatch[1])
402
+ : null;
403
+ if (gapValue !== null && gapValue !== TOAST_CONTAINER_GAP_SPACING) {
404
+ // Find line number
405
+ let lineNumber = 0;
406
+ for (let i = 0; i < lines.length; i++) {
407
+ if (/gap/.test(lines[i]) &&
408
+ /toast|notification/i.test(content.slice(0, content.indexOf(lines[i])))) {
409
+ lineNumber = i + 1;
410
+ break;
411
+ }
412
+ }
413
+ issues.push({
414
+ file: filePath,
415
+ line: lineNumber || undefined,
416
+ type: "toast-container-wrong-gap",
417
+ severity: "warning",
418
+ message: `Toast container uses gap-${gapValue} but expected gap-${TOAST_CONTAINER_GAP_SPACING}`,
419
+ suggestion: `Change to gap: "var(--spacing-${TOAST_CONTAINER_GAP_SPACING})" or className="gap-${TOAST_CONTAINER_GAP_SPACING}"`,
420
+ });
421
+ }
422
+ }
423
+ }
424
+ }
425
+ return issues;
426
+ }
427
+ /**
428
+ * Check 5: Find opportunities where hardcoded spacing values could use shared constants
429
+ *
430
+ * Scans components for patterns like:
431
+ * - gap: "var(--spacing-X)" that could use SPACING constants
432
+ * - padding: "var(--spacing-X)" that could use SPACING constants
433
+ * - Hardcoded layout component name arrays
434
+ *
435
+ * These are shown as WARNINGS to encourage consolidation.
436
+ */
437
+ async function checkSharedConstantOpportunities() {
438
+ const issues = [];
439
+ // Scan component files (not preflights - those are checked separately)
440
+ const componentFiles = await getAppComponentsTsxFiles();
441
+ // Spacing patterns to detect with their suggested constants
442
+ const spacingOpportunities = [
443
+ { value: 2, type: "gap", constant: "SPACING.COMPACT_GAP", minOccurrences: 2 },
444
+ { value: 3, type: "gap", constant: "SPACING.CARD_CONTAINER_GAP", minOccurrences: 2 },
445
+ { value: 6, type: "gap", constant: "SPACING.SECTION_GAP", minOccurrences: 2 },
446
+ { value: 4, type: "padding", constant: "SPACING.CONTENT_PADDING", minOccurrences: 3 },
447
+ { value: 6, type: "padding", constant: "SPACING.CARD_PADDING", minOccurrences: 2 },
448
+ ];
449
+ for (const file of componentFiles) {
450
+ // Skip layout files themselves - they're the source of truth
451
+ if (file.includes("Layout") && file.includes("components/shared"))
452
+ continue;
453
+ // Skip DashboardPageLayout specifically - it's the canonical source
454
+ if (file.includes("DashboardPageLayout"))
455
+ continue;
456
+ const content = fs.readFileSync(file, "utf8");
457
+ // Skip if file already imports SPACING from lib/constants
458
+ if (content.includes("from '@/lib/constants/spacing'") ||
459
+ content.includes('from "@/lib/constants/spacing"') ||
460
+ content.includes("from '@/lib/constants'") ||
461
+ content.includes('from "@/lib/constants"')) {
462
+ continue;
463
+ }
464
+ // Check each spacing pattern
465
+ for (const { value, type, constant, minOccurrences } of spacingOpportunities) {
466
+ const pattern = new RegExp(`${type}:\\s*["']var\\(--spacing-${value}\\)["']`, "g");
467
+ const matches = [...content.matchAll(pattern)];
468
+ if (matches.length >= minOccurrences) {
469
+ issues.push({
470
+ file,
471
+ type: `hardcoded-${type}-spacing-${value}`,
472
+ severity: "warning",
473
+ message: `${matches.length}x ${type}: "var(--spacing-${value})" ${console_chars_1.chars.arrow} use ${constant}`,
474
+ suggestion: `Import { SPACING } from '@/lib/constants/spacing'`,
475
+ });
476
+ }
477
+ }
478
+ // Check for files that define their own SPACING constants
479
+ if (/const\s+SPACING\s*=\s*\{/.test(content) && !file.includes("lib/constants")) {
480
+ issues.push({
481
+ file,
482
+ type: "duplicate-spacing-constant",
483
+ severity: "warning",
484
+ message: "Local SPACING constant - import from lib/constants/spacing instead",
485
+ suggestion: "Import { SPACING } from '@/lib/constants/spacing'",
486
+ });
487
+ }
488
+ // Check for hardcoded layout component arrays
489
+ if (/const\s+\w*(?:layouts?|pageLayouts?)\w*\s*=\s*\[\s*["'](?:Admin|Seller|Account|Dashboard)PageLayout["']/i.test(content)) {
490
+ issues.push({
491
+ file,
492
+ type: "hardcoded-layout-array",
493
+ severity: "warning",
494
+ message: "Hardcoded layout array - import GAP_LAYOUT_COMPONENTS",
495
+ suggestion: "Import from scripts/active/preflights/shared/layout-constants",
496
+ });
497
+ }
498
+ }
499
+ return issues;
500
+ }
501
+ /**
502
+ * Check 6: Find components using layout components that might benefit from shared constants
503
+ */
504
+ async function checkLayoutConsumerOpportunities() {
505
+ const issues = [];
506
+ const componentFiles = await getAppComponentsTsxFiles();
507
+ // Skip wrapper layout files - they legitimately use DashboardPageLayout
508
+ const wrapperFiles = ["AdminPageLayout", "SellerPageLayout", "AccountPageLayout"];
509
+ for (const file of componentFiles) {
510
+ const content = fs.readFileSync(file, "utf8");
511
+ // Skip if file already imports from layout-constants or lib/constants
512
+ if (content.includes("layout-constants") || content.includes("from '@/lib/constants"))
513
+ continue;
514
+ // Skip wrapper layout files
515
+ if (wrapperFiles.some((wrapper) => file.includes(wrapper)))
516
+ continue;
517
+ // Check if file uses multiple layout components (excluding wrapper patterns)
518
+ const layoutUsageCount = GAP_LAYOUT_COMPONENTS.filter((comp) => new RegExp(`<${comp}[\\s>]`).test(content)).length;
519
+ // Only flag if using 3+ different layouts (2 is common for wrapper pattern)
520
+ if (layoutUsageCount >= 3) {
521
+ issues.push({
522
+ file,
523
+ type: "multiple-layout-usage",
524
+ severity: "warning",
525
+ message: `Uses ${layoutUsageCount} different layout components - might benefit from shared constants`,
526
+ suggestion: "If checking layout types, consider importing GAP_LAYOUT_COMPONENTS",
527
+ });
528
+ }
529
+ // Check for switch/if statements checking layout component names
530
+ const layoutSwitchPattern = /(?:switch|if)\s*\([^)]*(?:Admin|Seller|Account|Dashboard)PageLayout/;
531
+ if (layoutSwitchPattern.test(content)) {
532
+ issues.push({
533
+ file,
534
+ type: "layout-type-checking",
535
+ severity: "warning",
536
+ message: "Contains layout type checking logic - could use GAP_LAYOUT_COMPONENTS",
537
+ suggestion: "Import GAP_LAYOUT_COMPONENTS for type-safe layout checking",
538
+ });
539
+ }
540
+ }
541
+ return issues;
542
+ }
543
+ async function run() {
544
+ const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(exports.name);
545
+ // Load project-specific dependencies
546
+ const loaded = await loadProjectDependencies();
547
+ if (!loaded) {
548
+ console.log(`${console_chars_1.emoji.skip} Skipping: project-specific modules not available`);
549
+ return { passed: true, skipped: true };
550
+ }
551
+ const args = process.argv.slice(2);
552
+ const verbose = args.includes("--verbose") || args.includes("-v");
553
+ console.log(`\n${console_chars_1.emoji.refresh} LAYOUT CONSTANTS SYNC PREFLIGHT`);
554
+ console.log(`${console_chars_1.box.doubleHorizontal}`.repeat(70));
555
+ console.log(`Shared Constants: GAP_LAYOUT_COMPONENTS has ${GAP_LAYOUT_COMPONENTS.length} components`);
556
+ console.log(`Expected Gap: spacing-${LAYOUT_GAP_SPACING} (${LAYOUT_GAP_SPACING * 4}px)`);
557
+ console.log(`Expected PaddingTop: spacing-${LAYOUT_PADDING_TOP_SPACING} (${LAYOUT_PADDING_TOP_SPACING * 4}px)`);
558
+ console.log(`${console_chars_1.box.doubleHorizontal}`.repeat(70));
559
+ const allIssues = [];
560
+ // Run core checks (always run)
561
+ console.log(`\n${console_chars_1.emoji.search} Checking layout component implementations...`);
562
+ const implIssues = await checkLayoutComponentsImplementGap();
563
+ allIssues.push(...implIssues);
564
+ console.log(` Found ${implIssues.length} issue(s)`);
565
+ console.log(`${console_chars_1.emoji.search} Checking preflights use shared constants...`);
566
+ const preflightIssues = await checkPreflightsUseSharedConstants();
567
+ allIssues.push(...preflightIssues);
568
+ console.log(` Found ${preflightIssues.length} issue(s)`);
569
+ console.log(`${console_chars_1.emoji.search} Checking all registered components exist...`);
570
+ const existIssues = await checkLayoutComponentsExist();
571
+ allIssues.push(...existIssues);
572
+ console.log(` Found ${existIssues.length} issue(s)`);
573
+ console.log(`${console_chars_1.emoji.search} Checking for unregistered gap layouts...`);
574
+ const unregisteredIssues = await checkUnregisteredGapLayouts();
575
+ allIssues.push(...unregisteredIssues);
576
+ console.log(` Found ${unregisteredIssues.length} issue(s)`);
577
+ console.log(`${console_chars_1.emoji.search} Checking toast container gap spacing...`);
578
+ const toastGapIssues = await checkToastContainerGaps();
579
+ allIssues.push(...toastGapIssues);
580
+ console.log(` Found ${toastGapIssues.length} issue(s)`);
581
+ // Run opportunity checks (always run - shown as warnings)
582
+ console.log(`${console_chars_1.emoji.search} Scanning for consolidation opportunities...`);
583
+ const constantOpportunities = await checkSharedConstantOpportunities();
584
+ const consumerOpportunities = await checkLayoutConsumerOpportunities();
585
+ allIssues.push(...constantOpportunities, ...consumerOpportunities);
586
+ console.log(` Found ${constantOpportunities.length + consumerOpportunities.length} opportunity warning(s)`);
587
+ // Report results
588
+ const errors = allIssues.filter((i) => i.severity === "error");
589
+ const warnings = allIssues.filter((i) => i.severity === "warning");
590
+ if (errors.length === 0 && warnings.length === 0) {
591
+ console.log(`\n${console_chars_1.emoji.success} Layout constants are in sync!`);
592
+ console.log(`${console_chars_1.box.doubleHorizontal}`.repeat(70));
593
+ process.exit(0);
594
+ }
595
+ // Print issues grouped by severity
596
+ if (errors.length > 0 || warnings.length > 0) {
597
+ console.log(`\n${console_chars_1.emoji.error} Found ${errors.length} error(s), ${warnings.length} warning(s):\n`);
598
+ }
599
+ // Group by file
600
+ const byFile = new Map();
601
+ for (const issue of allIssues) {
602
+ const existing = byFile.get(issue.file) || [];
603
+ existing.push(issue);
604
+ byFile.set(issue.file, existing);
605
+ }
606
+ for (const [file, fileIssues] of byFile) {
607
+ const fileErrors = fileIssues.filter((i) => i.severity === "error");
608
+ const fileWarnings = fileIssues.filter((i) => i.severity === "warning");
609
+ if (fileErrors.length > 0 || fileWarnings.length > 0) {
610
+ console.log(`${console_chars_1.emoji.folder} ${file}`);
611
+ for (const issue of [...fileErrors, ...fileWarnings]) {
612
+ const icon = issue.severity === "error" ? console_chars_1.emoji.error : console_chars_1.emoji.warning;
613
+ console.log(` ${icon} ${issue.line ? `Line ${issue.line}: ` : ""}${issue.message}`);
614
+ if (issue.suggestion && verbose) {
615
+ console.log(` ${console_chars_1.box.bottomLeft}${console_chars_1.box.horizontal} ${issue.suggestion}`);
616
+ }
617
+ }
618
+ console.log();
619
+ }
620
+ }
621
+ console.log(`${console_chars_1.box.doubleHorizontal}`.repeat(70));
622
+ if (errors.length > 0) {
623
+ console.log(`${console_chars_1.emoji.error} FAILED: ${errors.length} error(s) must be fixed`);
624
+ process.exit(1);
625
+ }
626
+ else if (warnings.length > 0) {
627
+ console.log(`${console_chars_1.emoji.warning} PASSED with ${warnings.length} warning(s) - consider using shared constants`);
628
+ if (!verbose) {
629
+ console.log(`${console_chars_1.emoji.hint} Run with --verbose to see suggestions`);
630
+ }
631
+ process.exit(0);
632
+ }
633
+ else {
634
+ console.log(`${console_chars_1.emoji.success} Layout constants are in sync!`);
635
+ process.exit(0);
636
+ }
637
+ }
638
+ // Allow direct execution
639
+ if (require.main === module) {
640
+ run().catch(console.error);
641
+ }
642
+ //# sourceMappingURL=layout-constants-sync.js.map