pkg-scaffold 3.2.0 → 3.3.2

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.
@@ -0,0 +1,376 @@
1
+ /**
2
+ * ============================================================================
3
+ * Headless API for pkg-scaffold v4.0.0
4
+ * ============================================================================
5
+ * Provides a programmatic interface for integrating pkg-scaffold into
6
+ * custom workflows, CI/CD pipelines, and third-party tools.
7
+ *
8
+ * Features:
9
+ * - Full control over analysis and refactoring operations
10
+ * - Event-driven architecture for real-time feedback
11
+ * - Streaming results for large codebases
12
+ * - Plugin SDK integration
13
+ */
14
+
15
+ import EventEmitter from 'events';
16
+ import { RefactoringEngine } from '../index.js';
17
+
18
+ export class HeadlessAPI extends EventEmitter {
19
+ constructor(options = {}) {
20
+ super();
21
+ this.options = options;
22
+ this.engine = null;
23
+ this.analysisResults = null;
24
+ this.isRunning = false;
25
+ }
26
+
27
+ /**
28
+ * Initialize the API with a project context
29
+ * @param {string} projectRoot - Root directory of the project
30
+ * @param {Object} config - Configuration options
31
+ * @returns {Promise<void>}
32
+ */
33
+ async initialize(projectRoot, config = {}) {
34
+ try {
35
+ this.emit('initialize:start', { projectRoot });
36
+
37
+ const engineOptions = {
38
+ cwd: projectRoot,
39
+ allowAutoFix: config.autoFix !== false,
40
+ skipConfirm: config.skipConfirm || false,
41
+ verbose: config.verbose || false,
42
+ ...config
43
+ };
44
+
45
+ this.engine = new RefactoringEngine(engineOptions);
46
+ await this.engine.context.initialize();
47
+
48
+ this.emit('initialize:complete', {
49
+ projectRoot,
50
+ config: this.engine.context
51
+ });
52
+ } catch (error) {
53
+ this.emit('initialize:error', { error });
54
+ throw error;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Analyze the codebase without making changes
60
+ * @returns {Promise<Object>} Analysis results
61
+ */
62
+ async analyze() {
63
+ if (!this.engine) {
64
+ throw new Error('API not initialized. Call initialize() first.');
65
+ }
66
+
67
+ try {
68
+ this.isRunning = true;
69
+ this.emit('analysis:start');
70
+
71
+ // Initialize path mappings and workspace graph
72
+ await this.engine.pathMapper.loadMappings(this.engine.context.tsconfigFilename);
73
+
74
+ if (this.engine.context.isWorkspaceEnabled) {
75
+ this.emit('analysis:workspace-mapping-start');
76
+ await this.engine.workspaceGraph.initializeWorkspaceMesh();
77
+ this.emit('analysis:workspace-mapping-complete');
78
+ }
79
+
80
+ // Load cache manifest
81
+ const cacheManifest = await this.engine.cacheManager.loadCacheManifest();
82
+
83
+ // Discover source files
84
+ this.emit('analysis:file-discovery-start');
85
+ const fileList = [];
86
+ await this.engine.discoverSourceFiles(this.engine.context.cwd, fileList);
87
+ this.engine.context.metrics.totalFilesScanned = fileList.length;
88
+ this.emit('analysis:file-discovery-complete', { fileCount: fileList.length });
89
+
90
+ // Identify framework ecosystems
91
+ this.emit('analysis:framework-detection-start');
92
+ const activeFrameworkEcosystems = await this.engine.magicDetector.identifyActiveProjectEcosystems(
93
+ this.engine.context.cwd
94
+ );
95
+ this.emit('analysis:framework-detection-complete', { ecosystems: activeFrameworkEcosystems });
96
+
97
+ // Process files
98
+ this.emit('analysis:file-processing-start');
99
+ const sourceCodeFilesList = [];
100
+ for (const file of fileList) {
101
+ if (file.endsWith('package.json')) {
102
+ await this.engine.auditManifestSupplyChain(file);
103
+ } else {
104
+ sourceCodeFilesList.push(file);
105
+ }
106
+ }
107
+
108
+ // Initialize TypeScript program for AST analysis (required before processFile)
109
+ if (sourceCodeFilesList.length > 0) {
110
+ try {
111
+ this.engine.analyzer.initProgram(sourceCodeFilesList);
112
+ } catch (e) {
113
+ if (this.engine.context.verbose) {
114
+ console.warn('Warning: Failed to initialize TypeScript program:', e.message);
115
+ }
116
+ }
117
+ }
118
+
119
+ // Parallel processing
120
+ let parallelParseCompleted = false;
121
+ if (sourceCodeFilesList.length > 10) {
122
+ parallelParseCompleted = await this.engine.workerPool.parallelAnalyzeCodebase(
123
+ sourceCodeFilesList,
124
+ this.engine
125
+ );
126
+ }
127
+
128
+ // Sequential processing
129
+ for (let i = 0; i < sourceCodeFilesList.length; i++) {
130
+ const filePath = sourceCodeFilesList[i];
131
+ const node = this.engine.context.createNode(filePath);
132
+ const currentHash = await this.engine.cacheManager.computeHash(filePath);
133
+ node.contentHash = currentHash;
134
+
135
+ const isFileCached = cacheManifest[filePath] && cacheManifest[filePath].hash === currentHash;
136
+
137
+ if (isFileCached) {
138
+ this.engine.context.metrics.cacheHits++;
139
+ this.engine.hydrateNodeFromCache(node, cacheManifest[filePath]);
140
+ } else if (!parallelParseCompleted) {
141
+ this.engine.context.metrics.cacheMisses++;
142
+ await this.engine.analyzer.processFile(filePath, node);
143
+ }
144
+
145
+ this.engine.magicDetector.injectVirtualConsumerEdges(filePath, node, activeFrameworkEcosystems);
146
+ node.externalPackageUsage.forEach(pkg => this.engine.context.usedExternalPackages.add(pkg));
147
+
148
+ // Emit progress
149
+ if ((i + 1) % Math.ceil(sourceCodeFilesList.length / 10) === 0) {
150
+ this.emit('analysis:file-processing-progress', {
151
+ processed: i + 1,
152
+ total: sourceCodeFilesList.length,
153
+ percentage: Math.round(((i + 1) / sourceCodeFilesList.length) * 100)
154
+ });
155
+ }
156
+ }
157
+
158
+ // Link dependency graph
159
+ this.emit('analysis:graph-linking-start');
160
+ await this.engine.linkDependencyGraph();
161
+ this.emit('analysis:graph-linking-complete');
162
+
163
+ // Generate summary
164
+ this.analysisResults = this.engine.context.generateSummaryReport();
165
+ this.emit('analysis:complete', this.analysisResults);
166
+
167
+ this.isRunning = false;
168
+ return this.analysisResults;
169
+ } catch (error) {
170
+ this.isRunning = false;
171
+ this.emit('analysis:error', { error });
172
+ throw error;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Get detailed impact analysis for a specific file or export
178
+ * @param {string} filePath - Path to the file
179
+ * @param {string} symbol - Optional: specific export symbol to analyze
180
+ * @returns {Promise<Object>} Impact analysis results
181
+ */
182
+ async getImpactAnalysis(filePath, symbol = null) {
183
+ if (!this.engine) {
184
+ throw new Error('API not initialized. Call initialize() first.');
185
+ }
186
+
187
+ try {
188
+ const node = this.engine.context.graph.get(filePath);
189
+ if (!node) {
190
+ throw new Error(`File not found in analysis graph: ${filePath}`);
191
+ }
192
+
193
+ const impact = {
194
+ file: filePath,
195
+ symbol,
196
+ directDependents: Array.from(node.incomingEdges),
197
+ dependencies: Array.from(node.outgoingEdges),
198
+ internalExports: symbol
199
+ ? [symbol]
200
+ : Array.from(node.internalExports.keys()),
201
+ externalPackages: Array.from(node.externalPackageUsage)
202
+ };
203
+
204
+ if (symbol) {
205
+ const safety = await this.engine.impactAnalyzer.verifyRefactorSafety(
206
+ filePath,
207
+ symbol,
208
+ this.engine.context.graph
209
+ );
210
+ impact.refactorSafety = safety;
211
+ }
212
+
213
+ return impact;
214
+ } catch (error) {
215
+ this.emit('impact-analysis:error', { error, filePath, symbol });
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Apply refactoring changes with automatic rollback on test failure
222
+ * @param {Object} changes - Changes to apply
223
+ * @returns {Promise<Object>} Refactoring results
224
+ */
225
+ async applyRefactoring(changes = {}) {
226
+ if (!this.engine) {
227
+ throw new Error('API not initialized. Call initialize() first.');
228
+ }
229
+
230
+ if (!this.analysisResults) {
231
+ throw new Error('No analysis results available. Call analyze() first.');
232
+ }
233
+
234
+ try {
235
+ this.isRunning = true;
236
+ this.emit('refactoring:start', changes);
237
+
238
+ const refactoringResults = {
239
+ filesDeleted: [],
240
+ exportsRemoved: [],
241
+ dependenciesRemoved: [],
242
+ errors: []
243
+ };
244
+
245
+ await this.engine.selfHealer.runSelfHealingLifecycle(async () => {
246
+ // Delete dead files
247
+ const filesToDelete = changes.deleteDeadFiles !== false
248
+ ? this.analysisResults.structuralIssuesDetected.deadFiles
249
+ : [];
250
+
251
+ for (const relPath of filesToDelete) {
252
+ try {
253
+ const absPath = require('path').resolve(this.engine.context.cwd, relPath);
254
+ await this.engine.txManager.stageDeletion(absPath);
255
+ refactoringResults.filesDeleted.push(relPath);
256
+ this.emit('refactoring:file-deleted', { file: relPath });
257
+ } catch (error) {
258
+ refactoringResults.errors.push({ type: 'file-deletion', file: relPath, error: error.message });
259
+ this.emit('refactoring:error', { type: 'file-deletion', file: relPath, error });
260
+ }
261
+ }
262
+
263
+ // Remove unused exports
264
+ const exportsToRemove = changes.removeUnusedExports !== false
265
+ ? this.analysisResults.structuralIssuesDetected.deadExports
266
+ : [];
267
+
268
+ for (const unusedExport of exportsToRemove) {
269
+ try {
270
+ const absPath = require('path').resolve(this.engine.context.cwd, unusedExport.file);
271
+ const node = this.engine.context.graph.get(absPath);
272
+
273
+ if (node) {
274
+ const safety = await this.engine.impactAnalyzer.verifyRefactorSafety(
275
+ absPath,
276
+ unusedExport.symbol,
277
+ this.engine.context.graph
278
+ );
279
+
280
+ if (safety.isSafeToPrune) {
281
+ const nextText = await this.engine.sourceRewriter.stripNamedExportSignature(
282
+ absPath,
283
+ unusedExport.symbol,
284
+ node.internalExports.get(unusedExport.symbol)
285
+ );
286
+
287
+ await this.engine.txManager.stageWrite(absPath, nextText);
288
+ await this.engine.typeIntegrity.synchronizeDeclarationFile(absPath, unusedExport.symbol);
289
+
290
+ refactoringResults.exportsRemoved.push(unusedExport);
291
+ this.emit('refactoring:export-removed', unusedExport);
292
+ }
293
+ }
294
+ } catch (error) {
295
+ refactoringResults.errors.push({
296
+ type: 'export-removal',
297
+ export: unusedExport.symbol,
298
+ file: unusedExport.file,
299
+ error: error.message
300
+ });
301
+ this.emit('refactoring:error', { type: 'export-removal', export: unusedExport, error });
302
+ }
303
+ }
304
+
305
+ // Remove unused dependencies
306
+ const depsToRemove = changes.removeUnusedDependencies !== false
307
+ ? this.analysisResults.structuralIssuesDetected.unusedDependencies
308
+ : [];
309
+
310
+ for (const dep of depsToRemove) {
311
+ try {
312
+ const absPath = require('path').resolve(this.engine.context.cwd, dep.manifest);
313
+ // TODO: Implement package.json dependency removal
314
+ refactoringResults.dependenciesRemoved.push(dep);
315
+ this.emit('refactoring:dependency-removed', dep);
316
+ } catch (error) {
317
+ refactoringResults.errors.push({
318
+ type: 'dependency-removal',
319
+ package: dep.package,
320
+ error: error.message
321
+ });
322
+ this.emit('refactoring:error', { type: 'dependency-removal', package: dep.package, error });
323
+ }
324
+ }
325
+ });
326
+
327
+ this.isRunning = false;
328
+ this.emit('refactoring:complete', refactoringResults);
329
+ return refactoringResults;
330
+ } catch (error) {
331
+ this.isRunning = false;
332
+ this.emit('refactoring:error', { error });
333
+ throw error;
334
+ }
335
+ }
336
+
337
+ /**
338
+ * Get current analysis metrics
339
+ * @returns {Object} Metrics
340
+ */
341
+ getMetrics() {
342
+ if (!this.engine) {
343
+ throw new Error('API not initialized. Call initialize() first.');
344
+ }
345
+ return this.engine.context.metrics;
346
+ }
347
+
348
+ /**
349
+ * Get all registered plugins
350
+ * @returns {Array} Plugin instances
351
+ */
352
+ getPlugins() {
353
+ if (!this.engine) {
354
+ throw new Error('API not initialized. Call initialize() first.');
355
+ }
356
+ return this.engine.context.pluginRegistry?.getPlugins() || [];
357
+ }
358
+
359
+ /**
360
+ * Get analysis results
361
+ * @returns {Object} Analysis results
362
+ */
363
+ getAnalysisResults() {
364
+ return this.analysisResults;
365
+ }
366
+
367
+ /**
368
+ * Check if API is currently running
369
+ * @returns {boolean}
370
+ */
371
+ isProcessing() {
372
+ return this.isRunning;
373
+ }
374
+ }
375
+
376
+ export default HeadlessAPI;
@@ -0,0 +1,299 @@
1
+ /**
2
+ * ============================================================================
3
+ * Plugin SDK for pkg-scaffold v4.0.0
4
+ * ============================================================================
5
+ * Provides utilities and helpers for developing custom plugins that extend
6
+ * pkg-scaffold's analysis and healing capabilities.
7
+ */
8
+
9
+ import { BasePlugin } from '../plugins/BasePlugin.js';
10
+
11
+ /**
12
+ * Extended plugin base class with SDK utilities
13
+ */
14
+ export class PluginSDKBase extends BasePlugin {
15
+ constructor(context) {
16
+ super(context);
17
+ this.hooks = new Map();
18
+ this.transformers = [];
19
+ this.validators = [];
20
+ }
21
+
22
+ /**
23
+ * Register a hook for a specific lifecycle event
24
+ * @param {string} eventName - Event name (e.g., 'analyze:start', 'refactor:complete')
25
+ * @param {Function} handler - Handler function
26
+ */
27
+ registerHook(eventName, handler) {
28
+ if (!this.hooks.has(eventName)) {
29
+ this.hooks.set(eventName, []);
30
+ }
31
+ this.hooks.get(eventName).push(handler);
32
+ }
33
+
34
+ /**
35
+ * Emit a hook event
36
+ * @param {string} eventName - Event name
37
+ * @param {Object} data - Event data
38
+ */
39
+ async emitHook(eventName, data) {
40
+ const handlers = this.hooks.get(eventName) || [];
41
+ for (const handler of handlers) {
42
+ await handler(data);
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Register a code transformer
48
+ * @param {Function} transformer - Transformer function
49
+ */
50
+ registerTransformer(transformer) {
51
+ this.transformers.push(transformer);
52
+ }
53
+
54
+ /**
55
+ * Register a validator
56
+ * @param {Function} validator - Validator function
57
+ */
58
+ registerValidator(validator) {
59
+ this.validators.push(validator);
60
+ }
61
+
62
+ /**
63
+ * Apply all registered transformers to a code string
64
+ * @param {string} code - Source code
65
+ * @param {string} filePath - File path
66
+ * @returns {Promise<string>} Transformed code
67
+ */
68
+ async applyTransformers(code, filePath) {
69
+ let result = code;
70
+ for (const transformer of this.transformers) {
71
+ result = await transformer(result, filePath);
72
+ }
73
+ return result;
74
+ }
75
+
76
+ /**
77
+ * Run all validators on a code string
78
+ * @param {string} code - Source code
79
+ * @param {string} filePath - File path
80
+ * @returns {Promise<Array>} Validation errors
81
+ */
82
+ async runValidators(code, filePath) {
83
+ const errors = [];
84
+ for (const validator of this.validators) {
85
+ const result = await validator(code, filePath);
86
+ if (result && result.length > 0) {
87
+ errors.push(...result);
88
+ }
89
+ }
90
+ return errors;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * SDK utilities for plugin development
96
+ */
97
+ export class PluginSDK {
98
+ /**
99
+ * Create a custom plugin class
100
+ * @param {Object} config - Plugin configuration
101
+ * @returns {Class} Plugin class
102
+ */
103
+ static createPlugin(config) {
104
+ return class CustomPlugin extends PluginSDKBase {
105
+ get name() {
106
+ return config.name;
107
+ }
108
+
109
+ getConfigFiles() {
110
+ return config.configFiles || [];
111
+ }
112
+
113
+ getRoutePatterns() {
114
+ return config.routePatterns || [];
115
+ }
116
+
117
+ getRequiredSystemContracts() {
118
+ return config.requiredContracts || ['default'];
119
+ }
120
+
121
+ async isActive(baseDir) {
122
+ if (config.isActive) {
123
+ return await config.isActive(baseDir);
124
+ }
125
+ return super.isActive(baseDir);
126
+ }
127
+
128
+ async initialize() {
129
+ if (config.initialize) {
130
+ await config.initialize(this.context);
131
+ }
132
+ }
133
+
134
+ async analyze(node, filePath) {
135
+ if (config.analyze) {
136
+ return await config.analyze(node, filePath, this.context);
137
+ }
138
+ }
139
+
140
+ async transform(code, filePath) {
141
+ if (config.transform) {
142
+ return await config.transform(code, filePath, this.context);
143
+ }
144
+ return code;
145
+ }
146
+
147
+ async validate(code, filePath) {
148
+ if (config.validate) {
149
+ return await config.validate(code, filePath, this.context);
150
+ }
151
+ return [];
152
+ }
153
+ };
154
+ }
155
+
156
+ /**
157
+ * Create a CSS-in-JS analyzer plugin
158
+ * @param {Object} config - Configuration for CSS-in-JS analysis
159
+ * @returns {Class} Plugin class
160
+ */
161
+ static createCSSInJSPlugin(config = {}) {
162
+ const cssLibraries = config.libraries || [
163
+ 'styled-components',
164
+ 'emotion',
165
+ '@emotion/react',
166
+ '@emotion/styled',
167
+ 'linaria',
168
+ 'vanilla-extract'
169
+ ];
170
+
171
+ return this.createPlugin({
172
+ name: config.name || 'css-in-js-analyzer',
173
+ configFiles: [],
174
+ routePatterns: [/\.(tsx?|jsx?)$/],
175
+ requiredContracts: [],
176
+
177
+ async analyze(node, filePath) {
178
+ // Track CSS-in-JS imports
179
+ for (const lib of cssLibraries) {
180
+ if (node.explicitImports.has(lib)) {
181
+ node.cssInJsLibraries = node.cssInJsLibraries || new Set();
182
+ node.cssInJsLibraries.add(lib);
183
+ }
184
+ }
185
+
186
+ // Detect styled component definitions
187
+ const styledPattern = /(?:styled|css|keyframes)\s*\.\w+|styled\(\w+\)/g;
188
+ for (const match of (node.rawCode || '').matchAll(styledPattern)) {
189
+ node.styledComponentUsages = node.styledComponentUsages || [];
190
+ node.styledComponentUsages.push(match[0]);
191
+ }
192
+ },
193
+
194
+ async validate(code, filePath) {
195
+ const errors = [];
196
+ // Check for unused CSS-in-JS definitions
197
+ const unusedStylesPattern = /(?:const|let|var)\s+(\w+)\s*=\s*(?:styled|css)\./g;
198
+ const matches = [...code.matchAll(unusedStylesPattern)];
199
+
200
+ for (const match of matches) {
201
+ const styleName = match[1];
202
+ const usagePattern = new RegExp(`\\b${styleName}\\b`);
203
+ if (!usagePattern.test(code.substring(match.index + match[0].length))) {
204
+ errors.push({
205
+ type: 'unused-style',
206
+ name: styleName,
207
+ line: code.substring(0, match.index).split('\n').length,
208
+ message: `Unused CSS-in-JS definition: ${styleName}`
209
+ });
210
+ }
211
+ }
212
+
213
+ return errors;
214
+ }
215
+ });
216
+ }
217
+
218
+ /**
219
+ * Create an asset tracking plugin
220
+ * @param {Object} config - Configuration for asset tracking
221
+ * @returns {Class} Plugin class
222
+ */
223
+ static createAssetTrackingPlugin(config = {}) {
224
+ const assetExtensions = config.extensions || [
225
+ '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp',
226
+ '.mp4', '.webm', '.mp3', '.wav',
227
+ '.woff', '.woff2', '.ttf', '.eot'
228
+ ];
229
+
230
+ return this.createPlugin({
231
+ name: config.name || 'asset-tracker',
232
+ configFiles: [],
233
+ routePatterns: [/\.(tsx?|jsx?)$/],
234
+
235
+ async analyze(node, filePath) {
236
+ node.assetReferences = node.assetReferences || new Set();
237
+
238
+ // Track asset imports
239
+ const assetImportPattern = /import\s+(?:\*\s+as\s+\w+|[\w\s,{}]+)\s+from\s+['"]([^'"]+(?:${assetExtensions.join('|')}))['"]/g;
240
+ for (const match of (node.rawCode || '').matchAll(assetImportPattern)) {
241
+ node.assetReferences.add(match[1]);
242
+ }
243
+
244
+ // Track asset requires
245
+ const assetRequirePattern = /require\s*\(\s*['"]([^'"]+(?:${assetExtensions.join('|')}))['"]\s*\)/g;
246
+ for (const match of (node.rawCode || '').matchAll(assetRequirePattern)) {
247
+ node.assetReferences.add(match[1]);
248
+ }
249
+
250
+ // Track asset URLs in strings
251
+ const assetUrlPattern = /['"]([^'"]*(?:${assetExtensions.join('|')}))['"]/g;
252
+ for (const match of (node.rawCode || '').matchAll(assetUrlPattern)) {
253
+ node.assetReferences.add(match[1]);
254
+ }
255
+ }
256
+ });
257
+ }
258
+
259
+ /**
260
+ * Create a monorepo awareness plugin
261
+ * @param {Object} config - Configuration for monorepo support
262
+ * @returns {Class} Plugin class
263
+ */
264
+ static createMonorepoPlugin(config = {}) {
265
+ return this.createPlugin({
266
+ name: config.name || 'monorepo-aware',
267
+ configFiles: config.configFiles || ['nx.json', 'pnpm-workspace.yaml', 'lerna.json'],
268
+
269
+ async analyze(node, filePath) {
270
+ // Track workspace package references
271
+ node.workspaceReferences = node.workspaceReferences || new Set();
272
+
273
+ // Detect workspace imports (e.g., @workspace/package-name)
274
+ const workspacePattern = /@[\w-]+\/[\w-]+/g;
275
+ for (const match of (node.rawCode || '').matchAll(workspacePattern)) {
276
+ node.workspaceReferences.add(match[0]);
277
+ }
278
+ }
279
+ });
280
+ }
281
+
282
+ /**
283
+ * Create a circular dependency detector plugin
284
+ * @param {Object} config - Configuration
285
+ * @returns {Class} Plugin class
286
+ */
287
+ static createCircularDepPlugin(config = {}) {
288
+ return this.createPlugin({
289
+ name: config.name || 'circular-dep-detector',
290
+
291
+ async analyze(node, filePath) {
292
+ node.potentialCycles = node.potentialCycles || [];
293
+ // Cycle detection will be handled by the main engine
294
+ }
295
+ });
296
+ }
297
+ }
298
+
299
+ export default PluginSDK;
@@ -40,8 +40,9 @@ export class ASTAnalyzer {
40
40
  */
41
41
  async processFile(filePath, fileNode) {
42
42
  // Fast Path: Use OXC for rapid scanning if type checking is not strictly required for this file
43
- if (this.context.fastMode) {
44
- return await this.oxc.processFile(filePath, fileNode);
43
+ if (this.context.fastMode && this.oxc.isAvailable) {
44
+ const success = await this.oxc.processFile(filePath, fileNode);
45
+ if (success) return true;
45
46
  }
46
47
 
47
48
  if (!this.program) {