UNPKG

toloframework

Version:

Javascript/HTML/CSS compiler for Firefox OS or nodewebkit apps using modules in the nodejs style.

248 lines (229 loc) 7.58 kB
/** * @module source */ var FS = require("fs"); var Path = require("path"); var Fatal = require("./fatal"); /** * @class Source * @param {Project} prj {@link project~Project} * @param {string} file path to the source file, relative to the source path. * @see {@link project~Project} */ var Source = function(prj, file) { if (typeof prj === 'string') { throw Error("[Source()] First argument must be an instance of class Project!"); } var srcDir = prj.srcOrLibPath(); if (file.substr(0, srcDir.length) == srcDir) { // Make this file relative. file = file.substr(srcDir.length); } this._prj = prj; this._name = file; this._tmpFile = prj.tmpPath(file); if (!FS.existsSync(this._tmpFile)) { prj.mkdir(Path.dirname(this._tmpFile)); } this._absPath = prj.srcOrLibPath(file); this._tags = null; }; /** * @return void */ Source.prototype.exists = function() { if (!this._absPath) return false; return FS.existsSync(this._absPath); }; /** * @return name of the file. It can be `index.html` or `cls/wtag.Button.js`. */ Source.prototype.name = function() { return this._name; }; /** * @return the project ob */ Source.prototype.prj = function() { return this._prj; }; /** * @return an instance of Source based on the same project. */ Source.prototype.create = function(file) { return new Source(this._prj, file); }; /** * @return Text content of the source. */ Source.prototype.read = function() { if (typeof this._absPath !== 'string') { console.info("[source] this._absPath=...", this._absPath); this._prj.fatal("[Source.read] File not found: \"" + this._name + "\"!"); } return FS.readFileSync(this._absPath).toString(); }; /** * @return `true` if the tags' file exists and is more recent than the source file. */ Source.prototype.isUptodate = function() { if (!FS.existsSync(this._tmpFile)) { return false; } var statSrc = FS.statSync(this._absPath); var statTmp = FS.statSync(this._tmpFile); if (statSrc.mtime < statTmp.mtime) { if (this.name().substr(-3) != '.js') return true; // For JS files, we look for INTL file. var pathSrc = this.getAbsoluteFilePath(); var pathIni = pathSrc.substr(0, pathSrc.length - 2) + "ini"; // Check intl file (*.ini). if (typeof this.tag("intl") === 'string' && this.tag("intl").length == 0) { // If there is no intl file, JS is uptodate. if (!FS.existsSync(pathIni)) return true; return false; } else { if (FS.existsSync(pathIni)) { var statIni = FS.statSync(pathIni); if (statIni.mtime > statTmp.mtime) { // Intl file has changed. return false; } } else { // Intl file has been deleted. console.log("INI file is missing. Creating one: " + pathSrc); FS.writeFileSync( pathIni, "[en]\n\n[fr]\n\n" ); return false; } } // For JS files, we look for DEP file. var pathDep = pathSrc.substr(0, pathSrc.length - 2) + "dep"; if (FS.existsSync(pathDep)) { var statDep = FS.statSync(pathDep); if (statDep.mtime > statTmp.mtime) { // Dep file has changed. return false; } } // Look at watch files. There are files that produce the // `GLOBAL` variable. var watch = this.tag('watch') || []; var srcWatch, statWatch, k; for (k=0; k<watch.length; k++) { srcWatch = new Source(this.prj(), watch[k]); if (srcWatch.exists()) { statWatch = FS.statSync(srcWatch.getAbsoluteFilePath()); if (statWatch.mtime > statTmp.mtime) { console.log(watch[k] + " => ".cyan + this.name()); return false; } } } return true; } return false; }; /** * @return Last modification time of the source. */ Source.prototype.modificationTime = function() { if (!FS.existsSync(this._tmpFile)) { var statSrc = FS.statSync(this._absPath); return statSrc.mtime; } var statTmp = FS.statSync(this._tmpFile); return statTmp.mtime; }; /** * Store tags on disk and mark the source file as _uptodate_. */ Source.prototype.save = function() { FS.writeFileSync(this._tmpFile, JSON.stringify(this._tags || {})); }; /** * @return Absolute path of the source file. It can be in the project * `src/` file or in the ToloFrameWork `lib/` path. If it is nowhere, * return _null_. */ Source.prototype.getAbsoluteFilePath = function() { return this._absPath; }; Source.prototype.getPathRelativeToSource = function(relPath) { var dir = Path.dirname(this._absPath); if (typeof relPath !== 'string') { Fatal.fire( 'Argument `relPath` of function `getPathRelativeToSource()` must be a string!\n' + 'But actual value is of type `' + (typeof relPath) + '`.' ); } return Path.join(dir, relPath); }; /** * Get/Set tags. * Don't forget to call method `save()` to store tags on disk and mark the source file has _uptodate_. * @param {string} name name of the tag. * @param value (optional) if defined, the value to set. * @return If `value` is _undefined_, return the tag's value. */ Source.prototype.tag = function(name, value) { if (!this._tags) { if (FS.existsSync(this._tmpFile)) { try { this._tags = JSON.parse(FS.readFileSync(this._tmpFile).toString()); } catch (ex) { this._tags = {}; } } else { this._tags = {}; } } if (typeof value === 'undefined') { return this._tags[name]; } if (value === null) { delete this._tags[name]; } else { this._tags[name] = value; } }; /** * Resources are stored in a directory with the source's name without * extendion. For instance, if the source is "tunnel.css", resources * are stored in directory "tunnel/" in the same folder. * @return Array of resources relative to the source's folder. */ Source.prototype.listResources = function() { var dir = this.getAbsoluteFilePath(); var pos = dir.lastIndexOf('.'); if (pos > -1) { // Removing extension. dir = dir.substr(0, pos); } var root = Path.dirname(dir); var listFiles = function(path) { return FS.readdirSync(path).map( function(x) { return Path.join(path, x); } ); }; if (false == FS.existsSync(dir)) { return []; } var output = []; var fringe = listFiles(dir); while (fringe.length > 0) { var file = fringe.pop(); if (false == FS.existsSync(file)) continue; var stat = FS.statSync(file); if (stat.isDirectory()) { fringe = listFiles(file).concat(fringe); } else { output.push([file.substr(root.length + 1), file]); } } return output; }; /** * Provides the class `Source` used to add __tags__ on source files. */ module.exports = Source;