@@ -7,30 +7,140 @@ class AbstractBuilder {
77	/** 
88	 * Constructor 
99	 * 
10+ 	 * @param  {object } resourceCollections Resource collections 
11+ 	 * @param  {DuplexCollection } resourceCollections.workspace Workspace Resource 
12+ 	 * @param  {ReaderCollection } resourceCollections.dependencies Workspace Resource 
1013	 * @param  {object } project Project configuration 
1114	 * @param  {GroupLogger } parentLogger Logger to use 
1215	 */ 
13- 	constructor ( { project,  parentLogger} )  { 
14- 		this . tasks  =  { } ; 
16+ 	constructor ( { resourceCollections,  project,  parentLogger} )  { 
17+ 		if  ( new . target  ===  AbstractBuilder )  { 
18+ 			throw  new  TypeError ( "Class 'AbstractBuilder' is abstract" ) ; 
19+ 		} 
20+ 
21+ 		this . project  =  project ; 
22+ 
1523		this . log  =  parentLogger . createSubLogger ( project . type  +  " "  +  project . metadata . name ,  0.2 ) ; 
1624		this . taskLog  =  this . log . createTaskLogger ( "🔨" ) ; 
17- 		this . availableTasks  =  [ ] ; 
25+ 
26+ 		this . tasks  =  { } ; 
27+ 		this . taskExecutionOrder  =  [ ] ; 
28+ 		this . addStandardTasks ( { resourceCollections,  project} ) ; 
29+ 		this . addCustomTasks ( { resourceCollections,  project} ) ; 
30+ 	} 
31+ 
32+ 	/** 
33+ 	 * Adds all standard tasks to execute 
34+ 	 * 
35+ 	 * @abstract  
36+ 	 * @protected  
37+  	 * @param  {object } resourceCollections Resource collections 
38+ 	 * @param  {DuplexCollection } resourceCollections.workspace Workspace Resource 
39+ 	 * @param  {ReaderCollection } resourceCollections.dependencies Workspace Resource 
40+ 	 * @param  {object } project Project configuration 
41+ 	 */ 
42+ 	addStandardTasks ( )  { 
43+ 		throw  new  Error ( "Function 'addStandardTasks' is not implemented" ) ; 
44+ 	} 
45+ 
46+ 	/** 
47+ 	 * Adds custom tasks to execute 
48+ 	 * 
49+ 	 * @private  
50+  	 * @param  {object } resourceCollections Resource collections 
51+ 	 * @param  {DuplexCollection } resourceCollections.workspace Workspace Resource 
52+ 	 * @param  {ReaderCollection } resourceCollections.dependencies Workspace Resource 
53+ 	 * @param  {object } project Project configuration 
54+ 	 */ 
55+ 	addCustomTasks ( { resourceCollections,  project} )  { 
56+ 		const  projectCustomTasks  =  project . builder  &&  project . builder . customTasks ; 
57+ 		if  ( ! projectCustomTasks  ||  projectCustomTasks . length  ===  0 )  { 
58+ 			return ;  // No custom tasks defined 
59+ 		} 
60+ 		const  taskRepository  =  require ( "../tasks/taskRepository" ) ; 
61+ 		for  ( let  i  =  0 ;  i  <  projectCustomTasks . length ;  i ++ )  { 
62+ 			const  taskDef  =  projectCustomTasks [ i ] ; 
63+ 			if  ( ! taskDef . name )  { 
64+ 				throw  new  Error ( `Missing name for custom task definition of project ${ project . metadata . name }   `  + 
65+ 					`at index ${ i }  ` ) ; 
66+ 			} 
67+ 			if  ( taskDef . beforeTask  &&  taskDef . afterTask )  { 
68+ 				throw  new  Error ( `Custom task definition ${ taskDef . name }   of project ${ project . metadata . name }   `  + 
69+ 					`defines both "beforeTask" and "afterTask" parameters. Only one must be defined.` ) ; 
70+ 			} 
71+ 			if  ( ! taskDef . beforeTask  &&  ! taskDef . afterTask )  { 
72+ 				throw  new  Error ( `Custom task definition ${ taskDef . name }   of project ${ project . metadata . name }   `  + 
73+ 					`defines neither a "beforeTask" nor an "afterTask" parameter. One must be defined.` ) ; 
74+ 			} 
75+ 
76+ 			let  newTaskName  =  taskDef . name ; 
77+ 			if  ( this . tasks [ newTaskName ] )  { 
78+ 				// Task is already known 
79+ 				// => add a suffix to allow for multiple configurations of the same task 
80+ 				let  suffixCounter  =  0 ; 
81+ 				while  ( this . tasks [ newTaskName ] )  { 
82+ 					suffixCounter ++ ;  // Start at 1 
83+ 					newTaskName  =  `${ newTaskName }  --${ suffixCounter }  ` ; 
84+ 				} 
85+ 			} 
86+ 			// Create custom task if not already done (task might be referenced multiple times, first one wins) 
87+ 			const  task  =  taskRepository . getTask ( taskDef . name ) ; 
88+ 			const  execTask  =  function ( )  { 
89+ 				/* Custom Task Interface 
90+ 					Parameters: 
91+ 						{Object} parameters Parameters 
92+ 						{DuplexCollection} parameters.workspace DuplexCollection to read and write files 
93+ 						{AbstractReader} parameters.dependencies Reader or Collection to read dependency files 
94+ 						{Object} parameters.options Options 
95+ 						{string} parameters.options.projectName Project name 
96+ 						{string} [parameters.options.configuration] Task configuration if given in ui5.yaml 
97+ 					Returns: 
98+ 						{Promise<undefined>} Promise resolving with undefined once data has been written 
99+ 				*/ 
100+ 				return  task ( { 
101+ 					workspace : resourceCollections . workspace , 
102+ 					dependencies : resourceCollections . dependencies , 
103+ 					options : { 
104+ 						projectName : project . metadata . name , 
105+ 						configuration : taskDef . configuration 
106+ 					} 
107+ 				} ) ; 
108+ 			} ; 
109+ 
110+ 			this . tasks [ newTaskName ]  =  execTask ; 
111+ 
112+ 			const  refTaskName  =  taskDef . beforeTask  ||  taskDef . afterTask ; 
113+ 			let  refTaskIdx  =  this . taskExecutionOrder . indexOf ( refTaskName ) ; 
114+ 			if  ( refTaskIdx  ===  - 1 )  { 
115+ 				throw  new  Error ( `Could not find task ${ refTaskName }  , referenced by custom task ${ newTaskName }  , `  + 
116+ 					`to be scheduled for project ${ project . metadata . name }  ` ) ; 
117+ 			} 
118+ 			if  ( taskDef . afterTask )  { 
119+ 				// Insert after index of referenced task 
120+ 				refTaskIdx ++ ; 
121+ 			} 
122+ 			this . taskExecutionOrder . splice ( refTaskIdx ,  0 ,  newTaskName ) ; 
123+ 		} 
18124	} 
19125
20126	/** 
21127	 * Adds a executable task to the builder 
22128	 * 
23- 	 * This does not ensure the correct build order. The order is maintained through the property 
24- 	 * [availableTasks]{@link  AbstractBuilder#availableTasks} 
129+ 	 * The order this function is being called defines the build order. FIFO. 
25130	 * 
26131	 * @param  {string } taskName Name of the task which should be in the list availableTasks. 
27132	 * @param  {function } taskFunction 
28133	 */ 
29134	addTask ( taskName ,  taskFunction )  { 
30- 		if  ( this . availableTasks . indexOf ( taskName )  ===  - 1 )  { 
31- 			throw  new  Error ( `Task "${ taskName }  " does not exist.` ) ; 
135+ 		if  ( this . tasks [ taskName ] )  { 
136+ 			throw  new  Error ( `Failed to add duplicative task ${ taskName }   for project ${ this . project . metadata . name }  ` ) ; 
137+ 		} 
138+ 		if  ( this . taskExecutionOrder . includes ( taskName ) )  { 
139+ 			throw  new  Error ( `Builder: Failed ot add task ${ taskName }   for project ${ this . project . metadata . name }  . `  + 
140+ 				`It has already been scheduled for execution.` ) ; 
32141		} 
33142		this . tasks [ taskName ]  =  taskFunction ; 
143+ 		this . taskExecutionOrder . push ( taskName ) ; 
34144	} 
35145
36146	/** 
@@ -40,21 +150,17 @@ class AbstractBuilder {
40150	 * @returns  {Promise } Returns promise chain with tasks 
41151	 */ 
42152	build ( tasksToRun )  { 
43- 		const  allTasksCount  =  tasksToRun . filter ( ( value )  =>  this . availableTasks . includes ( value ) ) . length ; 
153+ 		const  allTasksCount  =  tasksToRun . filter ( ( value )  =>  this . tasks . hasOwnProperty ( value ) ) . length ; 
44154		this . taskLog . addWork ( allTasksCount ) ; 
45155
46156		let  taskChain  =  Promise . resolve ( ) ; 
47- 		for  ( let  i  =  0 ;  i  <  this . availableTasks . length ;  i ++ )  { 
48- 			const  taskName  =  this . availableTasks [ i ] ; 
49- 
50- 			if  ( ! tasksToRun . includes ( taskName ) )  { 
51- 				continue ; 
52- 			} 
53- 
54- 			const  taskFunction  =  this . tasks [ taskName ] ; 
157+ 		for  ( const  taskName  in  this . tasks )  { 
158+ 			if  ( this . tasks . hasOwnProperty ( taskName )  &&  tasksToRun . includes ( taskName ) )  { 
159+ 				const  taskFunction  =  this . tasks [ taskName ] ; 
55160
56- 			if  ( typeof  taskFunction  ===  "function" )  { 
57- 				taskChain  =  taskChain . then ( this . wrapTask ( taskName ,  taskFunction ) ) ; 
161+ 				if  ( typeof  taskFunction  ===  "function" )  { 
162+ 					taskChain  =  taskChain . then ( this . wrapTask ( taskName ,  taskFunction ) ) ; 
163+ 				} 
58164			} 
59165		} 
60166		return  taskChain ; 
0 commit comments