Skip to content

Commit

Permalink
[INTERNAL] AbstractBuilder: Refactoring & Introducing Build Phases
Browse files Browse the repository at this point in the history
  • Loading branch information
devtomtom committed Apr 6, 2018
1 parent d34a3b2 commit 291a997
Show file tree
Hide file tree
Showing 9 changed files with 172 additions and 235 deletions.
109 changes: 14 additions & 95 deletions lib/builder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ const definedTasks = {
generateLibraryPreload: require("../tasks/bundlers/generateLibraryPreload")
};


// Set of tasks for development
const devTasks = [
"replaceCopyright",
"replaceVersion",
"buildThemes"
];

/**
* Calculates the elapsed build time and returns a prettified output
*
Expand All @@ -50,29 +42,31 @@ function getElapsedTime(startTime) {
* @param {Object} parameters.tree Dependency tree
* @param {string} parameters.destPath Target path
* @param {boolean} [parameters.buildDependencies=false] Decides whether project dependencies are built as well
* @param {boolean} [parameters.dev=false] Decides whether a development build should be activated (skips non-essential and time-intensive tasks)
* @param {boolean} [parameters.basic=false] Decides whether a basic build should be activated (skips non-essential and time-intensive tasks)
* @param {boolean} [parameters.selfContained=false] Flag to activate self contained build
* @param {Array} [parameters.includedTasks=[]] List of tasks to be included
* @param {Array} [parameters.excludedTasks=[]] List of tasks to be excluced. If the wildcard '*' is provided, only the included tasks will be executed.
* @param {Array} [parameters.devExcludeProject=[]] List of projects to be exlcuded from development build
* @param {Array} [parameters.devExcludeProject=[]] List of projects to be exlcuded from basic build
* @returns {Promise<undefined>} Promise resolving to <code>undefined</code> once build has finished
*/
function build({tree, destPath, buildDependencies = false, dev = false, selfContained = false, includedTasks = [], excludedTasks = [], devExcludeProject = []}) {
function build({tree, destPath, buildDependencies = false, basic = false, selfContained = false, includedTasks = [], excludedTasks = [], devExcludeProject = []}) {
const startTime = process.hrtime();
log.info(`Building project ${tree.metadata.name}` + (buildDependencies ? "" : " not") +
" including dependencies..." + (dev ? " [dev mode]" : ""));
" including dependencies..." + (basic ? " [basic mode]" : ""));
log.verbose(`Building to ${destPath}...`);

let selectedTasks = composeTaskList({dev, selfContained, includedTasks, excludedTasks});

const fsTarget = resourceFactory.createAdapter({
fsBasePath: destPath,
virBasePath: "/"
});

// If both build options are set to true, only the basic build mode will be activated
if (basic && selfContained) {
log.info("Building project in basic mode.");
selfContained = false;
}

const projects = {}; // Unique project index to prevent building the same project multiple times

const projectCountMarker = {};
function projectCount(project, count = 0) {
if (buildDependencies) {
Expand All @@ -91,7 +85,6 @@ function build({tree, destPath, buildDependencies = false, dev = false, selfCont

function buildProject(project) {
let depPromise;
let projectTasks = selectedTasks;
if (buildDependencies) {
// Build dependencies in sequence as it is far easier to detect issues and reduces
// side effects or other issues such as too many open files
Expand Down Expand Up @@ -120,17 +113,17 @@ function build({tree, destPath, buildDependencies = false, dev = false, selfCont
name: project.metadata.name
});

if (dev && devExcludeProject.indexOf(project.metadata.name) !== -1) {
projectTasks = composeTaskList({dev: false, selfContained, includedTasks, excludedTasks});
}
return projectType.build({
resourceCollections: {
workspace,
dependencies: resourceCollections.dependencies
},
tasks: projectTasks,
project,
parentLogger: log
parentLogger: log,
buildOptions: {
basic: basic,
selfContained: selfContained
}
}).then(() => {
log.verbose("Finished building project %s. Writing out files...", project.metadata.name);
buildLogger.completeWork(1);
Expand All @@ -156,80 +149,6 @@ function build({tree, destPath, buildDependencies = false, dev = false, selfCont
throw err;
});
}
/**
* Creates the list of tasks to be executed by the build process
*
* Sets specific tasks to be disabled by default, these tasks need to be included explicitly.
* Based on the selected build mode (dev|selfContained|preload), different tasks are enabled.
* Tasks can be enabled or disabled. The wildcard <code>*</code> is also supported and affects all tasks.
*
* @private
* @param {boolean} dev Sets development mode, which only runs essential tasks
* @param {boolean} selfContained True if a the build should be self-contained or false for prelead build bundles
* @param {Array} includedTasks Task list to be included from build
* @param {Array} excludedTasks Task list to be exlcuded from build
* @returns {Array} Return a task list for the builder
*/
function composeTaskList({dev, selfContained, includedTasks, excludedTasks}) {
let selectedTasks = Object.keys(definedTasks).reduce((list, key) => {
list[key] = true;
return list;
}, {});

// Exclude tasks: manifestBundler
selectedTasks.generateManifestBundle = false;
selectedTasks.generateStandaloneAppBundle = false;

if (selfContained) {
// No preloads, bundle only
selectedTasks.generateAppPreload = false;
selectedTasks.generateStandaloneAppBundle = true;
selectedTasks.generateLibraryPreload = false;
}

// Only run essential tasks in development mode, it is not desired to run time consuming tasks during development.
if (dev) {
// Overwrite all other tasks with noop promise
Object.keys(selectedTasks).forEach((key) => {
if (devTasks.indexOf(key) === -1) {
selectedTasks[key] = false;
}
});
}

// Exclude tasks
for (let i = 0; i < excludedTasks.length; i++) {
let taskName = excludedTasks[i];
if (taskName === "*") {
Object.keys(selectedTasks).forEach((sKey) => {
selectedTasks[sKey] = false;
});
break;
}
if (selectedTasks[taskName] !== false) {
selectedTasks[taskName] = false;
}
}

// Include tasks
for (let i = 0; i < includedTasks.length; i++) {
let taskName = includedTasks[i];
if (taskName === "*") {
Object.keys(selectedTasks).forEach((sKey) => {
selectedTasks[sKey] = true;
});
break;
}
if (selectedTasks[taskName] === false) {
selectedTasks[taskName] = true;
}
}

// Filter only for tasks that will be executed
selectedTasks = Object.keys(selectedTasks).filter((task) => selectedTasks[task]);

return selectedTasks;
}

module.exports = {
build: build,
Expand Down
57 changes: 38 additions & 19 deletions lib/types/AbstractBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,39 @@ class AbstractBuilder {
* @param {object} project Project configuration
* @param {GroupLogger} parentLogger Logger to use
*/
constructor({project, parentLogger}) {
this.tasks = {};
constructor({resourceCollections, project, parentLogger, buildOptions}) {
this.resourceCollections = resourceCollections;
this.project = project;
this.buildOptions = buildOptions;
this.tasks = [];
this.log = parentLogger.createSubLogger(project.type + " " + project.metadata.name, 0.2);
this.taskLog = this.log.createTaskLogger("🔨");
this.availableTasks = [];
}

// Build Phases
preprocess() {}

themebuilding() {}

process() {}

bundle() {}

optimize() {}

/**
* Adds a executable task to the builder
*
* This does not ensure the correct build order. The order is maintained through the property
* [availableTasks]{@link AbstractBuilder#availableTasks}
* The build order depends on the order the build tasks get added to the project.
*
* @param {string} taskName Name of the task which should be in the list availableTasks.
* @param {string} taskName Name of the task
* @param {function} taskFunction
*/
addTask(taskName, taskFunction) {
if (this.availableTasks.indexOf(taskName) === -1) {
throw new Error(`Task "${taskName}" does not exist.`);
}
this.tasks[taskName] = taskFunction;
this.tasks.push({
taskName: taskName,
taskFunction: taskFunction
});
}

/**
Expand All @@ -39,20 +51,27 @@ class AbstractBuilder {
* @param {array} tasksToRun List of tasks which should be executed
* @returns {Promise} Returns promise chain with tasks
*/
build(tasksToRun) {
const allTasksCount = tasksToRun.filter((value) => this.availableTasks.includes(value)).length;
build() {
// Run phases and add tasks to queue
this.preprocess();
this.themebuilding();

if (!this.buildOptions.basic) {
this.process();
this.bundle();
this.optimize();
}

const allTasksCount = Object.keys(this.tasks).length;
this.taskLog.addWork(allTasksCount);

let taskChain = Promise.resolve();
for (let i = 0; i < this.availableTasks.length; i++) {
let taskName = this.availableTasks[i];

if (!tasksToRun.includes(taskName)) {
continue;
}

taskChain = taskChain.then(this.wrapTask(taskName, this.tasks[taskName]));
for (var i = 0; i < this.tasks.length; i++) {
var task = this.tasks[i];
taskChain = taskChain.then(this.wrapTask(task.taskName, task.taskFunction));
}

return taskChain;
}

Expand Down
Loading

0 comments on commit 291a997

Please sign in to comment.