@@ -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