@empline/preflight 1.1.20 → 1.1.22
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/checks/architecture/jsx-structure-duplication.d.ts +26 -0
- package/dist/checks/architecture/jsx-structure-duplication.d.ts.map +1 -0
- package/dist/checks/architecture/jsx-structure-duplication.js +343 -0
- package/dist/checks/architecture/jsx-structure-duplication.js.map +1 -0
- package/package.json +7 -5
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* JSX Structure Duplication Detection Preflight
|
|
4
|
+
*
|
|
5
|
+
* Detects duplicate JSX structures across files that should be extracted
|
|
6
|
+
* into shared components. Unlike component-consolidation-opportunities which
|
|
7
|
+
* uses predefined patterns, this check discovers arbitrary duplicate structures.
|
|
8
|
+
*
|
|
9
|
+
* How it works:
|
|
10
|
+
* 1. Parses JSX from files and extracts structural blocks
|
|
11
|
+
* 2. Creates normalized fingerprints (ignoring variable names, values)
|
|
12
|
+
* 3. Groups similar structures across files
|
|
13
|
+
* 4. Reports structures that appear 3+ times as consolidation candidates
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* pnpm preflight:jsx-duplication
|
|
17
|
+
* npx tsx src/checks/architecture/jsx-structure-duplication.ts --verbose
|
|
18
|
+
*/
|
|
19
|
+
export declare const id = "architecture/jsx-structure-duplication";
|
|
20
|
+
export declare const name = "JSX Structure Duplication Detection";
|
|
21
|
+
export declare const category = "architecture";
|
|
22
|
+
export declare const blocking = false;
|
|
23
|
+
export declare const description = "Detects duplicate JSX structures that should be extracted into shared components";
|
|
24
|
+
export declare const tags: string[];
|
|
25
|
+
export declare function run(): Promise<void>;
|
|
26
|
+
//# sourceMappingURL=jsx-structure-duplication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-structure-duplication.d.ts","sourceRoot":"","sources":["../../../src/checks/architecture/jsx-structure-duplication.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;GAgBG;AAWH,eAAO,MAAM,EAAE,2CAA2C,CAAC;AAC3D,eAAO,MAAM,IAAI,wCAAwC,CAAC;AAC1D,eAAO,MAAM,QAAQ,iBAAiB,CAAC;AACvC,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,WAAW,qFAC4D,CAAC;AACrF,eAAO,MAAM,IAAI,UAA6E,CAAC;AAgR/F,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAsDzC"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* JSX Structure Duplication Detection Preflight
|
|
5
|
+
*
|
|
6
|
+
* Detects duplicate JSX structures across files that should be extracted
|
|
7
|
+
* into shared components. Unlike component-consolidation-opportunities which
|
|
8
|
+
* uses predefined patterns, this check discovers arbitrary duplicate structures.
|
|
9
|
+
*
|
|
10
|
+
* How it works:
|
|
11
|
+
* 1. Parses JSX from files and extracts structural blocks
|
|
12
|
+
* 2. Creates normalized fingerprints (ignoring variable names, values)
|
|
13
|
+
* 3. Groups similar structures across files
|
|
14
|
+
* 4. Reports structures that appear 3+ times as consolidation candidates
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* pnpm preflight:jsx-duplication
|
|
18
|
+
* npx tsx src/checks/architecture/jsx-structure-duplication.ts --verbose
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.tags = exports.description = exports.blocking = exports.category = exports.name = exports.id = void 0;
|
|
55
|
+
exports.run = run;
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const crypto = __importStar(require("crypto"));
|
|
58
|
+
const glob_1 = require("glob");
|
|
59
|
+
const console_chars_1 = require("../../utils/console-chars");
|
|
60
|
+
const universal_progress_reporter_1 = require("../system/universal-progress-reporter");
|
|
61
|
+
const glob_patterns_1 = require("../../shared/glob-patterns");
|
|
62
|
+
// Check metadata
|
|
63
|
+
exports.id = "architecture/jsx-structure-duplication";
|
|
64
|
+
exports.name = "JSX Structure Duplication Detection";
|
|
65
|
+
exports.category = "architecture";
|
|
66
|
+
exports.blocking = false;
|
|
67
|
+
exports.description = "Detects duplicate JSX structures that should be extracted into shared components";
|
|
68
|
+
exports.tags = ["architecture", "duplication", "shared-components", "jsx", "refactoring"];
|
|
69
|
+
// Configuration
|
|
70
|
+
const CONFIG = {
|
|
71
|
+
// Minimum occurrences to report as duplicate
|
|
72
|
+
minOccurrences: 3,
|
|
73
|
+
// Minimum lines for a JSX block to be considered
|
|
74
|
+
minBlockLines: 5,
|
|
75
|
+
// Maximum lines for a JSX block (too large = probably not a reusable component)
|
|
76
|
+
maxBlockLines: 50,
|
|
77
|
+
// Directories to scan
|
|
78
|
+
includeDirs: ["app", "components"],
|
|
79
|
+
// File patterns to exclude
|
|
80
|
+
excludePatterns: [
|
|
81
|
+
...glob_patterns_1.CORE_EXCLUDES,
|
|
82
|
+
"**/components/shared/**", // Already shared
|
|
83
|
+
"**/components/ui/**", // Primitive components
|
|
84
|
+
"**/*.test.tsx",
|
|
85
|
+
"**/*.spec.tsx",
|
|
86
|
+
"**/*.stories.tsx",
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
// JSX Block Detection
|
|
90
|
+
/**
|
|
91
|
+
* Extract JSX blocks from file content.
|
|
92
|
+
* Looks for multi-line JSX structures wrapped in parentheses or fragments.
|
|
93
|
+
*/
|
|
94
|
+
function extractJSXBlocks(content, filePath) {
|
|
95
|
+
const blocks = [];
|
|
96
|
+
const lines = content.split("\n");
|
|
97
|
+
// Pattern to detect start of JSX blocks:
|
|
98
|
+
// - Opening tag with attributes spanning multiple lines
|
|
99
|
+
// - Fragment openings
|
|
100
|
+
// - Conditional JSX: {condition && (<JSX>...)}
|
|
101
|
+
// - Ternary JSX: {condition ? (<JSX>...) : ...}
|
|
102
|
+
const jsxStartPatterns = [
|
|
103
|
+
// Conditional rendering with opening paren
|
|
104
|
+
/\{[^}]*&&\s*\(\s*$/,
|
|
105
|
+
/\{[^}]*\?\s*\(\s*$/,
|
|
106
|
+
// Return statement with JSX
|
|
107
|
+
/return\s*\(\s*$/,
|
|
108
|
+
// Variable assignment with JSX
|
|
109
|
+
/=\s*\(\s*$/,
|
|
110
|
+
// Opening Card/Box/Stack/div with className
|
|
111
|
+
/<(?:Card|Box|Stack|div|Paper|Container)[^>]*className[^>]*>\s*$/,
|
|
112
|
+
];
|
|
113
|
+
let blockStart = null;
|
|
114
|
+
let depth = 0;
|
|
115
|
+
let currentBlock = [];
|
|
116
|
+
for (let i = 0; i < lines.length; i++) {
|
|
117
|
+
const line = lines[i];
|
|
118
|
+
const trimmed = line.trim();
|
|
119
|
+
// Check if this starts a new JSX block
|
|
120
|
+
if (blockStart === null) {
|
|
121
|
+
for (const pattern of jsxStartPatterns) {
|
|
122
|
+
if (pattern.test(line)) {
|
|
123
|
+
blockStart = i;
|
|
124
|
+
depth = 1;
|
|
125
|
+
currentBlock = [line];
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
// We're inside a block, track depth
|
|
132
|
+
currentBlock.push(line);
|
|
133
|
+
// Count opening/closing parens and tags
|
|
134
|
+
const openParens = (line.match(/\(/g) || []).length;
|
|
135
|
+
const closeParens = (line.match(/\)/g) || []).length;
|
|
136
|
+
depth += openParens - closeParens;
|
|
137
|
+
// Also track JSX tag depth for self-contained components
|
|
138
|
+
const openTags = (line.match(/<[A-Z][^/>]*(?<!\/)\s*>/g) || []).length;
|
|
139
|
+
const closeTags = (line.match(/<\/[A-Z][^>]*>/g) || []).length;
|
|
140
|
+
const selfClosing = (line.match(/<[A-Z][^>]*\/>/g) || []).length;
|
|
141
|
+
// Check if block is complete
|
|
142
|
+
if (depth <= 0 || (trimmed.startsWith("</") && closeTags > openTags)) {
|
|
143
|
+
const blockLines = currentBlock.length;
|
|
144
|
+
if (blockLines >= CONFIG.minBlockLines && blockLines <= CONFIG.maxBlockLines) {
|
|
145
|
+
const raw = currentBlock.join("\n");
|
|
146
|
+
const normalized = normalizeJSX(raw);
|
|
147
|
+
const fingerprint = hashString(normalized);
|
|
148
|
+
blocks.push({
|
|
149
|
+
file: filePath,
|
|
150
|
+
startLine: blockStart + 1, // 1-indexed
|
|
151
|
+
endLine: i + 1,
|
|
152
|
+
raw,
|
|
153
|
+
normalized,
|
|
154
|
+
fingerprint,
|
|
155
|
+
context: currentBlock[0].trim().substring(0, 60),
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
blockStart = null;
|
|
159
|
+
depth = 0;
|
|
160
|
+
currentBlock = [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return blocks;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Normalize JSX for comparison by removing variable specifics
|
|
167
|
+
* while preserving structure.
|
|
168
|
+
*/
|
|
169
|
+
function normalizeJSX(jsx) {
|
|
170
|
+
return (jsx
|
|
171
|
+
// Remove all whitespace
|
|
172
|
+
.replace(/\s+/g, " ")
|
|
173
|
+
// Normalize string values
|
|
174
|
+
.replace(/"[^"]*"/g, '"STRING"')
|
|
175
|
+
.replace(/'[^']*'/g, '"STRING"')
|
|
176
|
+
// Normalize variable names in expressions
|
|
177
|
+
.replace(/\{[a-z_][a-zA-Z0-9_]*\}/gi, "{VAR}")
|
|
178
|
+
// Normalize function calls
|
|
179
|
+
.replace(/\{[a-z_][a-zA-Z0-9_]*\([^)]*\)\}/gi, "{FUNC()}")
|
|
180
|
+
// Normalize property access
|
|
181
|
+
.replace(/\{[a-z_][a-zA-Z0-9_]*\.[a-zA-Z0-9_.]+\}/gi, "{VAR.PROP}")
|
|
182
|
+
// Normalize numbers
|
|
183
|
+
.replace(/\{[0-9]+\}/g, "{NUM}")
|
|
184
|
+
// Normalize conditional expressions but keep structure
|
|
185
|
+
.replace(/\{[^}]+\s*\?\s*/g, "{COND ? ")
|
|
186
|
+
.replace(/\s*:\s*[^}]+\}/g, " : ALT}")
|
|
187
|
+
// Normalize onClick/onChange handlers
|
|
188
|
+
.replace(/on[A-Z][a-zA-Z]*=\{[^}]+\}/g, "onEVENT={HANDLER}")
|
|
189
|
+
// Normalize className values but keep the attribute
|
|
190
|
+
.replace(/className="[^"]+"/g, 'className="CLASSES"')
|
|
191
|
+
// Normalize key props
|
|
192
|
+
.replace(/key=\{[^}]+\}/g, "key={KEY}")
|
|
193
|
+
// Trim
|
|
194
|
+
.trim());
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Create a hash of the normalized JSX for grouping
|
|
198
|
+
*/
|
|
199
|
+
function hashString(str) {
|
|
200
|
+
return crypto.createHash("md5").update(str).digest("hex").substring(0, 12);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Extract a suggested component name from the JSX structure
|
|
204
|
+
*/
|
|
205
|
+
function suggestComponentName(blocks) {
|
|
206
|
+
// Look for common patterns in the JSX
|
|
207
|
+
const sample = blocks[0].raw;
|
|
208
|
+
// Check for common UI patterns
|
|
209
|
+
if (/Resume.*Import/i.test(sample))
|
|
210
|
+
return "ResumeImportPrompt";
|
|
211
|
+
if (/Empty.*State/i.test(sample))
|
|
212
|
+
return "EmptyStateCard";
|
|
213
|
+
if (/Loading.*Spinner/i.test(sample))
|
|
214
|
+
return "LoadingIndicator";
|
|
215
|
+
if (/Confirm.*Dialog/i.test(sample))
|
|
216
|
+
return "ConfirmationCard";
|
|
217
|
+
if (/Error.*Message/i.test(sample))
|
|
218
|
+
return "ErrorDisplay";
|
|
219
|
+
if (/Success.*Message/i.test(sample))
|
|
220
|
+
return "SuccessDisplay";
|
|
221
|
+
if (/Filter.*Bar/i.test(sample))
|
|
222
|
+
return "FilterBar";
|
|
223
|
+
if (/Search.*Input/i.test(sample))
|
|
224
|
+
return "SearchBar";
|
|
225
|
+
if (/Stat.*Card/i.test(sample))
|
|
226
|
+
return "StatCard";
|
|
227
|
+
if (/Progress.*Bar/i.test(sample))
|
|
228
|
+
return "ProgressIndicator";
|
|
229
|
+
// Check for the root component type
|
|
230
|
+
const rootMatch = sample.match(/<([A-Z][a-zA-Z]+)/);
|
|
231
|
+
if (rootMatch) {
|
|
232
|
+
return `Shared${rootMatch[1]}`;
|
|
233
|
+
}
|
|
234
|
+
return "SharedComponent";
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Generate a preview of the structure for display
|
|
238
|
+
*/
|
|
239
|
+
function generateStructurePreview(block) {
|
|
240
|
+
const lines = block.raw.split("\n").slice(0, 8);
|
|
241
|
+
return lines.map((l) => " " + l.trim()).join("\n") + (block.raw.split("\n").length > 8 ? "\n ..." : "");
|
|
242
|
+
}
|
|
243
|
+
// Main Detection Logic
|
|
244
|
+
async function detectDuplicates() {
|
|
245
|
+
const allBlocks = [];
|
|
246
|
+
// Gather all files
|
|
247
|
+
const files = [];
|
|
248
|
+
for (const dir of CONFIG.includeDirs) {
|
|
249
|
+
const matches = await (0, glob_1.glob)(`${dir}/**/*.tsx`, {
|
|
250
|
+
ignore: CONFIG.excludePatterns,
|
|
251
|
+
cwd: process.cwd(),
|
|
252
|
+
});
|
|
253
|
+
files.push(...matches);
|
|
254
|
+
}
|
|
255
|
+
console.log(`${console_chars_1.emoji.folder} Scanning ${files.length} files for JSX structures...`);
|
|
256
|
+
// Extract blocks from each file
|
|
257
|
+
for (const file of files) {
|
|
258
|
+
try {
|
|
259
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
260
|
+
const blocks = extractJSXBlocks(content, file);
|
|
261
|
+
allBlocks.push(...blocks);
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Skip unreadable files
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
console.log(`${console_chars_1.emoji.search} Found ${allBlocks.length} JSX blocks to analyze`);
|
|
268
|
+
// Group by fingerprint
|
|
269
|
+
const groups = new Map();
|
|
270
|
+
for (const block of allBlocks) {
|
|
271
|
+
if (!groups.has(block.fingerprint)) {
|
|
272
|
+
groups.set(block.fingerprint, []);
|
|
273
|
+
}
|
|
274
|
+
groups.get(block.fingerprint).push(block);
|
|
275
|
+
}
|
|
276
|
+
// Filter to duplicates (3+ occurrences in different files)
|
|
277
|
+
const duplicates = [];
|
|
278
|
+
for (const [fingerprint, blocks] of groups) {
|
|
279
|
+
// Get unique files
|
|
280
|
+
const uniqueFiles = new Set(blocks.map((b) => b.file));
|
|
281
|
+
if (uniqueFiles.size >= CONFIG.minOccurrences) {
|
|
282
|
+
duplicates.push({
|
|
283
|
+
fingerprint,
|
|
284
|
+
blocks: blocks.slice(0, 5), // Limit to 5 examples
|
|
285
|
+
suggestedName: suggestComponentName(blocks),
|
|
286
|
+
structurePreview: generateStructurePreview(blocks[0]),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Sort by number of occurrences (most duplicated first)
|
|
291
|
+
duplicates.sort((a, b) => b.blocks.length - a.blocks.length);
|
|
292
|
+
return duplicates;
|
|
293
|
+
}
|
|
294
|
+
// Main Runner
|
|
295
|
+
async function run() {
|
|
296
|
+
const reporter = (0, universal_progress_reporter_1.createUniversalProgressReporter)(exports.name);
|
|
297
|
+
const startTime = Date.now();
|
|
298
|
+
const args = process.argv.slice(2);
|
|
299
|
+
const verbose = args.includes("--verbose") || args.includes("-v");
|
|
300
|
+
console.log(`\n${console_chars_1.emoji.search} JSX Structure Duplication Detection`);
|
|
301
|
+
console.log((0, console_chars_1.createDivider)(70, "heavy"));
|
|
302
|
+
console.log(`${console_chars_1.emoji.info} Looking for duplicate JSX structures that should be shared components\n`);
|
|
303
|
+
const duplicates = await detectDuplicates();
|
|
304
|
+
console.log((0, console_chars_1.createDivider)(70, "light"));
|
|
305
|
+
if (duplicates.length === 0) {
|
|
306
|
+
console.log(`\n${console_chars_1.emoji.success} No duplicate JSX structures detected`);
|
|
307
|
+
console.log(` Your codebase has good component reuse!\n`);
|
|
308
|
+
process.exit(0);
|
|
309
|
+
}
|
|
310
|
+
console.log(`\n${console_chars_1.emoji.warning} Found ${duplicates.length} duplicate JSX structure(s)\n`);
|
|
311
|
+
console.log(`These patterns appear in 3+ files and should be extracted to shared components:\n`);
|
|
312
|
+
for (const group of duplicates) {
|
|
313
|
+
const fileCount = new Set(group.blocks.map((b) => b.file)).size;
|
|
314
|
+
console.log(`${console_chars_1.emoji.components} ${group.suggestedName} (found in ${fileCount} files)`);
|
|
315
|
+
console.log(` Fingerprint: ${group.fingerprint}`);
|
|
316
|
+
console.log(` Suggested location: components/shared/${group.suggestedName}.tsx\n`);
|
|
317
|
+
if (verbose) {
|
|
318
|
+
console.log(` Structure preview:`);
|
|
319
|
+
console.log(group.structurePreview);
|
|
320
|
+
console.log();
|
|
321
|
+
}
|
|
322
|
+
console.log(` Found in:`);
|
|
323
|
+
for (const block of group.blocks) {
|
|
324
|
+
console.log(` - ${block.file}:${block.startLine}-${block.endLine}`);
|
|
325
|
+
}
|
|
326
|
+
console.log();
|
|
327
|
+
}
|
|
328
|
+
console.log((0, console_chars_1.createDivider)(70, "heavy"));
|
|
329
|
+
console.log(`\n${console_chars_1.emoji.hint} Recommendations:`);
|
|
330
|
+
console.log(` 1. Create shared components for the patterns above`);
|
|
331
|
+
console.log(` 2. Use props to handle variations between usages`);
|
|
332
|
+
console.log(` 3. Export from components/shared/index.ts`);
|
|
333
|
+
console.log(` 4. Update all files to use the new shared component\n`);
|
|
334
|
+
const elapsed = Date.now() - startTime;
|
|
335
|
+
console.log(`${console_chars_1.emoji.clock} Completed in ${(elapsed / 1000).toFixed(1)}s`);
|
|
336
|
+
console.log(`${console_chars_1.emoji.info} This check is non-blocking (advisory only)\n`);
|
|
337
|
+
process.exit(0);
|
|
338
|
+
}
|
|
339
|
+
// Allow direct execution
|
|
340
|
+
if (require.main === module) {
|
|
341
|
+
run().catch(console.error);
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=jsx-structure-duplication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-structure-duplication.js","sourceRoot":"","sources":["../../../src/checks/architecture/jsx-structure-duplication.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empline/preflight",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.22",
|
|
4
4
|
"description": "Distributable preflight validation system with app-specific plugin support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,13 +19,15 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc",
|
|
22
|
-
"prebuild": "npm run check:hygiene",
|
|
22
|
+
"prebuild": "npm run check:hygiene && npm run check:metadata && npm run check:broken",
|
|
23
23
|
"dev": "tsc --watch",
|
|
24
24
|
"preflight": "tsx src/bin/preflight.ts",
|
|
25
|
-
"check:
|
|
26
|
-
"check:security": "tsx src/
|
|
27
|
-
"check:business": "tsx src/
|
|
25
|
+
"check:system": "tsx src/bin/preflight.ts --category system",
|
|
26
|
+
"check:security": "tsx src/bin/preflight.ts --category security",
|
|
27
|
+
"check:business": "tsx src/bin/preflight.ts --category business",
|
|
28
28
|
"check:hygiene": "tsx scripts/maintenance/module-boundary-validator.ts",
|
|
29
|
+
"check:metadata": "tsx src/checks/system/preflight-metadata-validator.ts",
|
|
30
|
+
"check:broken": "tsx src/checks/system/broken-preflight-detection.ts",
|
|
29
31
|
"check:hygiene:all": "tsx scripts/maintenance/module-boundary-validator.ts && tsx scripts/maintenance/workflow-composition-validator.ts && tsx scripts/maintenance/canonical-import-enforcer.ts",
|
|
30
32
|
"lint": "eslint src/",
|
|
31
33
|
"test": "vitest",
|