1
+ #!/usr/bin/env node
2
+
3
+ const fs = require ( 'fs' ) ;
4
+ const path = require ( 'path' ) ;
5
+
6
+ const executorUtilsPath = path . join ( __dirname , '../node_modules/nx/src/command-line/run/executor-utils.js' ) ;
7
+ const taskRunnerUtilsPath = path . join ( __dirname , '../node_modules/nx/src/tasks-runner/utils.js' ) ;
8
+
9
+ // Enhanced parseExecutor function with comprehensive logging
10
+ const patchedParseExecutor = `function parseExecutor(executorString) {
11
+ console.log('DEBUG: parseExecutor called with:', executorString);
12
+
13
+ // Capture call stack to identify which target is causing the issue
14
+ const stack = new Error().stack;
15
+ const caller = stack.split('\\n')[2]; // Get the calling function
16
+
17
+ if (!executorString) {
18
+ console.error('ERROR: parseExecutor received undefined or null executorString');
19
+ console.error('ERROR: Call stack:', stack);
20
+ console.error('ERROR: Caller:', caller);
21
+
22
+ // Look for task object in arguments
23
+ const args = Array.from(arguments);
24
+ console.error('ERROR: Function arguments:', args);
25
+
26
+ throw new Error(\`parseExecutor received undefined or null executorString. Caller: \${caller}\`);
27
+ }
28
+
29
+ if (typeof executorString !== 'string') {
30
+ console.error('ERROR: parseExecutor received non-string executorString:', typeof executorString, executorString);
31
+ console.error('ERROR: Call stack:', stack);
32
+ console.error('ERROR: Caller:', caller);
33
+
34
+ throw new Error(\`parseExecutor received non-string executorString: \${typeof executorString}. Caller: \${caller}\`);
35
+ }
36
+
37
+ const result = executorString.split(':');
38
+ console.log('DEBUG: parseExecutor result:', result);
39
+ return result;
40
+ }` ;
41
+
42
+ // Enhanced getExecutorForTask with better undefined handling
43
+ const enhancedGetExecutorForTask = `function getExecutorForTask(task, projectGraph) {
44
+ console.log('DEBUG: getExecutorForTask - task:', task.id);
45
+
46
+ const executor = getExecutorNameForTask(task, projectGraph);
47
+ console.log('DEBUG: getExecutorForTask - executor resolved:', executor, 'for task:', task.id);
48
+
49
+ // Check for undefined, null, or empty string
50
+ if (executor === undefined || executor === null || executor === '') {
51
+ console.error('ERROR: getExecutorForTask - No executor found for task:', task.id);
52
+ console.error('ERROR: Task details:', JSON.stringify(task, null, 2));
53
+ console.error('ERROR: Project configuration:', JSON.stringify(projectGraph.nodes[task.target.project]?.data?.targets?.[task.target.target], null, 2));
54
+ console.error('ERROR: Available targets for project:',
55
+ projectGraph.nodes[task.target.project]?.data?.targets ?
56
+ Object.keys(projectGraph.nodes[task.target.project].data.targets) :
57
+ 'No targets found');
58
+ throw new Error(\`No executor found for task: \${task.id}\`);
59
+ }
60
+
61
+ // Log the task details before calling parseExecutor to help identify which task causes the issue
62
+ console.log('DEBUG: About to call parseExecutor for task:', task.id, 'with executor:', executor);
63
+ console.log('DEBUG: Task target:', JSON.stringify(task.target, null, 2));
64
+
65
+ try {
66
+ const [nodeModule, executorName] = (0, executor_utils_1.parseExecutor)(executor);
67
+ return (0, executor_utils_1.getExecutorInformation)(nodeModule, executorName, workspace_root_1.workspaceRoot, (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(projectGraph).projects);
68
+ } catch (error) {
69
+ console.error('ERROR: parseExecutor failed for task:', task.id);
70
+ console.error('ERROR: Task details:', JSON.stringify(task, null, 2));
71
+ console.error('ERROR: Executor value:', executor);
72
+ console.error('ERROR: Original error:', error.message);
73
+ throw error;
74
+ }
75
+ }` ;
76
+
77
+ function patchExecutorUtils ( ) {
78
+ try {
79
+ // Patch executor-utils.js
80
+ if ( ! fs . existsSync ( executorUtilsPath ) ) {
81
+ console . error ( 'ERROR: executor-utils.js not found at:' , executorUtilsPath ) ;
82
+ process . exit ( 1 ) ;
83
+ }
84
+
85
+ const originalExecutorUtilsContent = fs . readFileSync ( executorUtilsPath , 'utf8' ) ;
86
+
87
+ // Check if already patched
88
+ if ( ! originalExecutorUtilsContent . includes ( 'DEBUG: parseExecutor called with:' ) ) {
89
+ // Find the parseExecutor function and replace it
90
+ const parseExecutorRegex = / f u n c t i o n p a r s e E x e c u t o r \( e x e c u t o r S t r i n g \) \{ [ ^ } ] * \} / ;
91
+
92
+ if ( ! parseExecutorRegex . test ( originalExecutorUtilsContent ) ) {
93
+ console . error ( 'ERROR: Could not find parseExecutor function in executor-utils.js' ) ;
94
+ process . exit ( 1 ) ;
95
+ }
96
+
97
+ const patchedExecutorUtilsContent = originalExecutorUtilsContent . replace ( parseExecutorRegex , patchedParseExecutor ) ;
98
+
99
+ // Create backup
100
+ const backupPath = executorUtilsPath + '.backup' ;
101
+ if ( ! fs . existsSync ( backupPath ) ) {
102
+ fs . writeFileSync ( backupPath , originalExecutorUtilsContent ) ;
103
+ console . log ( 'INFO: Created backup at:' , backupPath ) ;
104
+ }
105
+
106
+ // Write patched version
107
+ fs . writeFileSync ( executorUtilsPath , patchedExecutorUtilsContent ) ;
108
+ console . log ( 'SUCCESS: Patched executor-utils.js with enhanced logging' ) ;
109
+ } else {
110
+ console . log ( 'INFO: executor-utils.js already patched' ) ;
111
+ }
112
+
113
+ // Enhanced patch for task-runner utils.js
114
+ if ( ! fs . existsSync ( taskRunnerUtilsPath ) ) {
115
+ console . error ( 'ERROR: task-runner utils.js not found at:' , taskRunnerUtilsPath ) ;
116
+ process . exit ( 1 ) ;
117
+ }
118
+
119
+ const taskRunnerUtilsContent = fs . readFileSync ( taskRunnerUtilsPath , 'utf8' ) ;
120
+
121
+ // Check if we need to update getExecutorForTask with enhanced logging
122
+ if ( ! taskRunnerUtilsContent . includes ( 'DEBUG: getExecutorForTask - executor resolved:' ) ) {
123
+ // Find and replace the getExecutorForTask function with enhanced version
124
+ const getExecutorForTaskRegex = / f u n c t i o n g e t E x e c u t o r F o r T a s k \( t a s k , p r o j e c t G r a p h \) \{ [ \s \S ] * ?\} (? = \s * e x p o r t s \. g e t E x e c u t o r F o r T a s k | \s * f u n c t i o n | \s * $ ) / ;
125
+
126
+ if ( getExecutorForTaskRegex . test ( taskRunnerUtilsContent ) ) {
127
+ const enhancedTaskRunnerUtilsContent = taskRunnerUtilsContent . replace ( getExecutorForTaskRegex , enhancedGetExecutorForTask ) ;
128
+
129
+ // Create backup
130
+ const taskRunnerBackupPath = taskRunnerUtilsPath + '.backup' ;
131
+ if ( ! fs . existsSync ( taskRunnerBackupPath ) ) {
132
+ fs . writeFileSync ( taskRunnerBackupPath , taskRunnerUtilsContent ) ;
133
+ console . log ( 'INFO: Created backup at:' , taskRunnerBackupPath ) ;
134
+ }
135
+
136
+ // Write enhanced version
137
+ fs . writeFileSync ( taskRunnerUtilsPath , enhancedTaskRunnerUtilsContent ) ;
138
+ console . log ( 'SUCCESS: Enhanced task-runner utils.js with better undefined handling' ) ;
139
+ } else {
140
+ console . log ( 'INFO: Could not find getExecutorForTask function to enhance' ) ;
141
+ }
142
+ } else {
143
+ console . log ( 'INFO: task-runner utils.js already has enhanced logging' ) ;
144
+ }
145
+
146
+ } catch ( error ) {
147
+ console . error ( 'ERROR: Failed to patch NX files:' , error . message ) ;
148
+ process . exit ( 1 ) ;
149
+ }
150
+ }
151
+
152
+ if ( require . main === module ) {
153
+ patchExecutorUtils ( ) ;
154
+ }
155
+
156
+ module . exports = { patchExecutorUtils } ;
0 commit comments