Source: lib/generation.js

/*------------------------------------*\
	ASSETS GENERATION
\*------------------------------------*/
/* jslint node: true */

/**
 * Copy content of a project from NodeAtlas `templates/` folder into current directory.
 * @private
 * @function createTemplateProject
 * @memberOf NA#
 * @this NA
 * @param {NA~fallback} next Fallback function.
 */
exports.createTemplateProject = function (next) {
	 var NA = this,
		fs = NA.modules.fs,
		path = NA.modules.path,
		copyDir = NA.modules.copyDir,
		source = (typeof NA.configuration.create === "string") ? NA.configuration.create : "hello-world",
		sourcePath = path.join(NA.nodeatlasPath, "templates", source),
		destinationPath = path.join(NA.serverPath),
		data = {};

	if (NA.configuration.create) {

		/* Copy starting project from `templates/` directory. By default it is "hello-world". */
		if (sourcePath !== destinationPath && fs.existsSync(sourcePath)) {

			copyDir(sourcePath, destinationPath, function () {
				data.pathName = sourcePath;
				NA.log(NA.cliLabels.initCopy.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
				if (NA.afterNewProject) {
					NA.afterNewProject.call(NA);
				} else {
					process.exit(0);
				}
			});
		} else {
			data.initPath = source;
			NA.log(NA.cliLabels.initNotFound.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
			process.exit(0);
		}
	} else {

		/**
		 * Continue with this alternative way if main operation not possible.
		 * @callback NA~fallback
		 */
		next();
	}
};

/**
 * Open all pages for generate HTML render into `serverlessRelativePath`.
 * @private
 * @function generateHTML
 * @memberOf NA~
 * @param {NA} NA NodeAtlas instance.
 */
function generateHTML(NA) {
	var output = false;

	/* Avoid copy of `assetsRelativePath` into `serverlessRelativePath` even if serverlessRelativePath directory exist. */
	if (typeof NA.webconfig.output === "boolean") {

		/**
		 * Disable HTML generation mechanism.
		 * @public
		 * @alias output
		 * @type {boolean}
		 * @memberOf NA#webconfig
		 * @default true
		 */
		output = NA.webconfig.output;
	}

	/* Generation only if is configured to « true » in `generate` or set co command line. */
	if (NA.configuration.generate) {

		/* Generate all HTML files. */
		if (output) {
			NA.forEach(NA.webconfig.routes, function (currentUrl) {
				NA.prepareResponse(currentUrl, NA.webconfig.routes, undefined, undefined, function (locals, request, response) {
					NA.changeVariationsCommon(locals, request, response);
				});
			});
		}

		NA.generateAssets();
	}
}

/**
 * Crawl all routes and generate them after passed into `NA#controllers[].setRoutes` hook.
 * @private
 * @function initOutputs
 * @memberOf NA#
 * @this NA
 */
exports.initOutputs = function () {
	var NA = this;

	if (typeof NA.controllers[NA.webconfig.controller] !== 'undefined' &&
		typeof NA.controllers[NA.webconfig.controller].setRoutes !== 'undefined') {
			NA.controllers[NA.webconfig.controller].setRoutes.call(NA, function () {
				generateHTML(NA);
			});
	} else {
		generateHTML(NA);
	}
};

/**
 * Compress all assets for generate `assetsRelativePath` content into `serverlessRelativePath` and copy all `NA#statics` directories..
 * @private
 * @function generateAssets
 * @memberOf NA#
 * @this NA
 */
exports.generateAssets = function () {
	var NA = this,
		async = NA.modules.async;

	/* Generate all minified CSS, JS and Images files. */
	async.parallel([
		NA.cssCompilation.bind(NA),
		NA.jsObfuscation.bind(NA)
	], function () {

		/* Copy all content of `assetsRelativePath` and `statics` into `serverlessRelativePath` */
		async.parallel([
			NA.publicsGeneration.bind(NA),
			NA.staticsGeneration.bind(NA),
		], function () {

			if (NA.afterGeneration) {
				NA.afterGeneration.call(NA);
			} else {
				process.exit(0);
			}
		});
	});
};

/**
 * Copy all `public` file from `assetsRelativePath` to `serverlessRelativePath`.
 * @private
 * @function publicsGeneration
 * @memberOf NA#
 * @this NA
 * @param {NA~callback} next Next step after copy of all `assetsRelativePath` content.
 */
exports.publicsGeneration = function (next) {
	var NA = this,
		path = NA.modules.path,
		fs = NA.modules.fs,
		copyDir = NA.modules.copyDir,
		sourcePath = path.join(NA.serverPath, NA.webconfig.assetsRelativePath),
		destinationPath = path.join(NA.serverPath, NA.webconfig.serverlessRelativePath),
		assetsCopy = false;

	/* Avoid copy of `assetsRelativePath` into `serverlessRelativePath` even if serverlessRelativePath directory exist. */
	if (typeof NA.webconfig.assetsCopy === "boolean") {

		/**
		 * Disable Assets generation mechanism.
		 * @public
		 * @alias assetsCopy
		 * @type {boolean}
		 * @memberOf NA#webconfig
		 * @default true
		 */
		assetsCopy = NA.webconfig.assetsCopy;
	}

	if (assetsCopy && sourcePath !== destinationPath && fs.existsSync(sourcePath)) {
		copyDir(sourcePath, destinationPath, function () {
			NA.log(NA.cliLabels.assetsCopy);
			next();
		});
	} else {
		next();
	}
};

/**
 * Copy mechanism for statics.
 * @private
 * @function traverse
 * @memberOf NA~
 * @this NA
 * @param {Object}      paths         Contain a `statics` content.
 * @param {string}      paths.real    The path of source directory.
 * @param {string}      paths.virtual The path of virtual url.
 * @param {boolean}     paths.output  Inform if copy is required
 * @param {NA~callback} next          Go to next steps.
 */
function traverse(paths, next) {
	var NA = this,
		copyDir = NA.modules.copyDir,
		path = NA.modules.path,
		sourcePath = path.join(NA.serverPath, paths.real),
		destinationPath = path.join(NA.serverPath, NA.webconfig.serverlessRelativePath, paths.virtual),
		data = {
			source: sourcePath,
			dest: destinationPath
		};

	if (paths.output) {
		copyDir(sourcePath, destinationPath, function () {
			NA.log(NA.cliLabels.staticsCopy.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return data[matches]; }));
			next();
		});
	} else {
		next();
	}
}

/**
 * Copy all `statics` form real directory to `serverlessRelativePath`.
 * @private
 * @function staticsGeneration
 * @memberOf NA#
 * @this NA
 * @param {NA~callback} next Next step after copy of all `assetsRelativePath` content.
 */
exports.staticsGeneration = function (next) {
	var NA = this,
		async = NA.modules.async,
		statics = [];

	if (NA.webconfig.statics) {

		NA.forEach(NA.webconfig.statics, function (directory, directories) {
			var virtual = directory,
				real = directories[directory],
				output = false;

			if (NA.webconfig.statics instanceof Array) {
				virtual = directory.virtual;
				output = directory.output;
				real = directory;
			}

			if (typeof real === "object") {
				output = (typeof real.output === "boolean" && !real.output) ? false : true;
				real = real.path;
			}

			statics.push({
				real: real,
				virtual: virtual,
				output: output
			});
		});

		async.map(statics, traverse.bind(NA), function() {
			next();
		});
	} else {
		next();
	}
};

/**
 * Save the render.
 * @private
 * @function save
 * @memberOf NA~
 * @param {NA}     NA           NodeAtlas instance.
 * @param {string} html         HTML result to save.
 * @param {string} data         Result if html is not a HTML content.
 * @param {string} templateName Name for register file.
 */
function save(NA, html, data, templateName) {
	var mkpath = NA.modules.mkpath,
		path = NA.modules.path,
		pathToSaveFileComplete = path.join(NA.serverPath, NA.webconfig.serverlessRelativePath, templateName),
		pathToSaveFile = path.dirname(pathToSaveFileComplete),
		fs = NA.modules.fs;

	if (templateName) {

		/* Create file render. */
		mkpath(pathToSaveFile, function () {
			var dataError = {};

			/* If source is not a HTML format, keep initial data format. */
			if (data.trim().match(/<\/html>$/g) === null) {
				html = data;
			}

			dataError.pathName = path.join(NA.serverPath, NA.webconfig.serverlessRelativePath, templateName);

			/* Write file */
			fs.writeFile(pathToSaveFileComplete, html, function (error) {
				if (error) {
					if (error.code === 'EISDIR' || error.code === 'ENOENT') {
						NA.log(NA.cliLabels.templateNotGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return dataError[matches]; }));
					} else {
						throw error;
					}
				} else {
					NA.log(NA.cliLabels.templateGenerate.replace(/%([\-a-zA-Z0-9_]+)%/g, function (regex, matches) { return dataError[matches]; }));
				}
			});

		});
	}
}

/**
 * Generate a template into an HTML file in folder `serverlessRelativePath`.
 * @private
 * @function saveRender
 * @memberOf NA#
 * @this NA
 * @param {string} data         Content of file generated.
 * @param {string} templateName The filename of file generated.
 */
exports.saveRender = function (data, templateName) {
	var NA = this,
		jsdom = NA.modules.jsdom,
		dom = new jsdom.JSDOM(data),
		deeper,
		newBase = "";

	/*
	 * If false, no generate for this line.
	 */
	if (templateName !== false) {

		/*
		 * If templateName ending with "/", remove it.
		 */
		templateName = templateName.replace(/\/$/g, "");

		/*
		 * If a <base> markup exist, calculation of
		 * relative placement of page under root folder...
		 */
		deeper = templateName.split("/").length - 1;
		if (templateName[0] === "/") {
			deeper = templateName.split("/").length - 2;
		}

		/* ...and creation of path for all resources */
		for (var i = 0; i < deeper; i++) {
			newBase += "../";
		}

		/* ...and set new base */
		Array.prototype.forEach.call(dom.window.document.getElementsByTagName("base"), function (base) {
			base.setAttribute("href", newBase.replace(/\/$/, ""));
		});

		/* Save the render. */
		save(NA, dom.serialize(), data, (templateName) ? templateName : "");
	}
};