@@ -7,30 +7,140 @@ class AbstractBuilder {
7
7
/**
8
8
* Constructor
9
9
*
10
+ * @param {object } resourceCollections Resource collections
11
+ * @param {DuplexCollection } resourceCollections.workspace Workspace Resource
12
+ * @param {ReaderCollection } resourceCollections.dependencies Workspace Resource
10
13
* @param {object } project Project configuration
11
14
* @param {GroupLogger } parentLogger Logger to use
12
15
*/
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
+
15
23
this . log = parentLogger . createSubLogger ( project . type + " " + project . metadata . name , 0.2 ) ;
16
24
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
+ }
18
124
}
19
125
20
126
/**
21
127
* Adds a executable task to the builder
22
128
*
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.
25
130
*
26
131
* @param {string } taskName Name of the task which should be in the list availableTasks.
27
132
* @param {function } taskFunction
28
133
*/
29
134
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.` ) ;
32
141
}
33
142
this . tasks [ taskName ] = taskFunction ;
143
+ this . taskExecutionOrder . push ( taskName ) ;
34
144
}
35
145
36
146
/**
@@ -40,21 +150,17 @@ class AbstractBuilder {
40
150
* @returns {Promise } Returns promise chain with tasks
41
151
*/
42
152
build ( tasksToRun ) {
43
- const allTasksCount = tasksToRun . filter ( ( value ) => this . availableTasks . includes ( value ) ) . length ;
153
+ const allTasksCount = tasksToRun . filter ( ( value ) => this . tasks . hasOwnProperty ( value ) ) . length ;
44
154
this . taskLog . addWork ( allTasksCount ) ;
45
155
46
156
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 ] ;
55
160
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
+ }
58
164
}
59
165
}
60
166
return taskChain ;
0 commit comments