aq-web-front
Version:
A compilation framework to allow coding web frontends in a modern style.
123 lines (116 loc) • 4.66 kB
JavaScript
var fs = require('fs');
var path = require('path');
var _ = require('underscore');
var fsext = require('../fsext');
var CompileProcess = require('./compile-process');
module.exports = Compiler;
/**
* `Compiler` is the class for compiling awfco (AWF Compiled Object).
*
* An instance of this class is responsible to compile (transpile) some type of
* source to valid es5 script code along with bundled css, module dependency and
* source map.
*
* This class should not be subclassed. Pass in a `compileFunc` when instantiating
* instead.
*
* `compileFunc` is an function implementing specific compile process. It will be
* called as an object method, the object will contain the following property and
* methods which may be accessed through this:
*
* opt: compiler input options with the following keys:
* moduleName : default import/export name for the module
* importPath : path of the module used for importing
* relativePath : path of the source file relative to project root
* absolutePath : system normalized absolute file path of the source file
* sourceContent : the text content of the source code
* extra : extra options that may be passed to the compiler
*
* success(): called on a successful compile, marks the end of compilation.
* Tip: set result on jsOutput, jsMap, cssOutput, cssMap defined on `this`
*
* reject(): called on failure (e.g. syntax error), marks the end of compilation.
* reject(message:string, line:number, column:number)
* reject(message:string, pos:Object<{path, content, line, column}>)
* reject(errors:Array)
*
* depends(): called if this compiled module logically depends on another compiled
* module and that depended module should precede in the linking process.
* depends(moduleName:string)
*
* watch(): called if this compiled object should be recompiled when a source file
* other than the directly pointed one is changed.
* watch(sourceAbsolutePath:string)
*
* Tip: The `compileFunc` function call context object can be extended by extending
* the prototype property of the function.
*
* Hint: all line and column numbers are zero based
**/
function Compiler(compileFunc) {
if (typeof this != 'object' || !(this instanceof Compiler)) {
return new Compiler(compileFunc);
}
this.CompileProcess = CompileProcess.derive(compileFunc);
}
/**
* Compile `fpath` to `target`, with `dir` as project root
* Return a promise
*/
Compiler.prototype.compile = function(fpath, target, dir, extra) {
return new Promise(proc.bind(this));
function proc(accept, reject) {
if (typeof dir == 'object') {
extra = dir;
dir = '.';
} else if (!dir) {
dir = '.';
}
fs.readFile(fpath, 'utf-8', gotFile.bind(this));
function gotFile(err, content) {
if (err) {
accept();
} else {
var opt = {};
opt.relativePath = fsext.normalizePath(path.relative(dir, fpath));
opt.absolutePath = fsext.normalizePath(path.resolve(fpath));
opt.target = fsext.normalizePath(path.resolve(target));
opt.moduleName = Compiler.moduleName(opt.relativePath);
opt.importPath = '/' + Compiler.importPath(opt.relativePath);
opt.extra = extra || {};
opt.sourceContent = content;
var compileProcess = new (this.CompileProcess)();
compileProcess
.compile(opt)
.then(accept)
.catch(function(err) {
console.warn(err.stack);
});
}
}
};
};
Compiler.moduleName = function(fpath) {
var path = require('path');
var name = path.basename(fpath);
name = name.split('.')[0] || '';
name = name.replace(/[^$a-zA-Z0-9_$]+/g, '_');
if (name.match(/^[0-9]/))
name = '_' + name;
return name;
};
Compiler.importPath = function(fpath, contextPath) {
var ret = fpath;
var isRelative = !!ret.match(/^\.\.?[\/\\]/);
var name = Compiler.moduleName(ret);
ret = path.dirname(ret);
if (name != 'index') {
ret = path.join(ret, name);
}
if (contextPath && isRelative) {
ret = path.join(path.dirname(contextPath), ret);
}
if (isRelative || fpath[0] == '/' || fpath[0] == '\\')
ret = '/' + ret;
return fsext.normalizePath(ret);
};