'use strict';
var hookLoaded = false;
var _ = require('./util.js');
var onCompileStart, onCompileEnd, onProcessEnd;
/**
* fis 整体编译入口。
* @param {Object} opt 配置项
* @param {Array} [opt.srcCache] 需要编译的文件列表,当没有填写时,fis 将通过 {@link fis.project.getSource}() 获取
* @param {Callback} [opt.beforeEach] 编译开始前执行的回调函数,无论走缓存与否。
* @param {Callback} [opt.afterEach] 编译完成后执行的回调函数,无论走缓存与否。
* @param {Callback} [opt.beforeCompile] 编译开始前执行,当采用缓存时不执行。
* @param {Callback} [opt.afterCompile] 编译完成后执行,当采用缓存时不执行。
* @param {Callback} [opt.beforeCacheRevert] 在缓存被应用到文件对象前执行。
* @param {Callback} [opt.afterCacheRevert] 在缓存被应用到文件对象后执行。
* @param {Callback} callback 当整体编译完成后执行。
* @function
* @memberOf fis
* @name release
*/
var exports = module.exports = function(opt, callback) {
if (typeof opt === 'function') {
callback = opt;
opt = {};
} else {
opt = opt || {};
}
var src = {};
if (Array.isArray(opt.srcCache) && opt.srcCache.length) {
opt.srcCache.forEach(function(path) {
if (!fis.util.isFile(path)) return;
var file = fis.file(path);
if (file.release) {
src[file.subpath] = file;
}
});
} else {
src = fis.project.getSource();
}
var ret = {
src: src,
ids: {},
pkg: {},
map: {
res: {},
pkg: {}
}
};
// 通过 env 的配置来决定。
var config = fis.media();
fis.compile.setup(opt);
// load hooks
hookLoaded || (fis.util.pipe('hook', function(processor, settings) {
processor(fis, settings);
}), hookLoaded = true);
fis.emit('release:start', ret);
var total = opt.total || {};
var resourceMapFiles = [];
var collect = function(file, type) {
type = type || 'res';
if (file.release && file.useMap) {
//add resource map
var id = file.getId();
ret.ids[id] = file;
var res = file.map = ret.map[type][id] = {
uri: file.getUrl(),
type: file.rExt.replace(/^\./, '')
};
for (var key in file.extras) {
if (file.extras.hasOwnProperty(key)) {
res.extras = file.extras;
break;
}
}
if (file.requires && file.requires.length) {
res.deps = file.requires;
}
}
if (file._isResourceMap) {
resourceMapFiles.push(file); // the resource map file is special
}
};
Object.keys(total).forEach(function(subpath) {
var file = total[subpath];
collect(file);
});
onCompileStart && fis.removeListener('compile:start', onCompileStart);
fis.on('compile:start', (onCompileStart = function(file) {
total[file.subpath] = file;
opt.beforeEach && opt.beforeEach(file, ret);
}));
onCompileEnd && fis.removeListener('compile:end', onCompileEnd);
fis.on('compile:end', (onCompileEnd = function(file) {
opt.afterEach && opt.afterEach(file, ret);
collect(file);
// 兼容 fis2 中的用法
if (file.extras && file.extras.derived) {
file.derived = file.derived.concat(file.extras.derived);
delete file.extras.derived;
}
if (!file.derived || !file.derived.length) {
return;
}
file.derived.forEach(function(obj) {
obj.__proto__ = file.__proto__;
total[obj.subpath] = obj;
obj.defineLikes();
opt.beforeEach && opt.beforeEach(obj);
opt.afterEach && opt.afterEach(obj, ret);
collect(obj);
});
}));
onProcessEnd && fis.removeListener('process:end', onProcessEnd);
fis.on('process:end', (onProcessEnd = function(file) {
if (file.useSameNameRequire) {
if (file.isJsLike) {
file.addSameNameRequire('.css');
} else if (file.isHtmlLike) {
file.addSameNameRequire('.js');
file.addSameNameRequire('.css');
}
}
}));
var pending = [];
var begining = ret.src;
ret.src = total;
Object.keys(begining).forEach(function(key) {
pending.push(begining[key]);
});
var onAddFileToCompile = function(file) {
pending.push(file);
};
fis.on('compile:add', onAddFileToCompile);
try {
while (pending.length) {
var file = pending.shift();
fis.compile(file, opt);
}
} finally {
fis.removeListener('compile:add', onAddFileToCompile);
}
// -----------
// 单文件已编译完成
//project root
var root = fis.project.getProjectPath();
//get pack config
var conf = config.get('pack');
if (typeof conf === 'undefined') {
//from fis-pack.json
var file = root + '/fis-pack.json';
if (fis.util.isFile(file)) {
fis.config.set('pack', conf = fis.util.readJSON(file));
}
}
if (typeof conf === 'undefined') {
conf = buildPack(ret);
}
// package callback
var cb = function(packager, settings, key, type) {
fis.log.debug('[' + key + '] start');
fis.emit(type, ret, conf, opt);
packager(ret, conf, settings, opt);
fis.log.debug('[' + key + '] end');
};
var packager = _.applyMatches('::package', fis.media().getSortedMatches());
['prepackager', 'packager', 'spriter', 'postpackager'].forEach(function(name) {
if (packager[name]) {
// fis.match 中配置的优先,所以,这里直接覆盖就行了。
fis.media().set('modules.' + name, packager[name]);
}
fis.util.pipe(name, cb, opt[name]);
});
// Object.keys(ret.pkg).forEach(function(subpath) {
// var file = ret.pkg[subpath];
// collect(file, 'pkg');
// });
// filling the resource map file, resourceMapFile is a reference file object
resourceMapFiles.forEach(function(file) {
file.setContent(
file.getContent()
.replace(
/\b__RESOURCE_MAP__\b/g,
JSON.stringify(ret.map, null, file.optimizer ? null : 4)
)
);
});
fis.emit('release:end', ret);
//done
if (callback) {
callback(ret);
}
};
/*
* 根据用户配置的 fis.match 生成 pack 表。
*/
function buildPack(ret) {
var src = ret.src;
var pack = {};
Object.keys(src).forEach(function(subpath) {
var file = src[subpath];
var packTo = file.packTo;
if (!packTo || file.release === false || file.isPartial) {
return;
}
pack[packTo] = pack[packTo] || [];
pack[packTo].push(file.subpath);
});
return pack;
}
Namespaces
- fis
- cache
- cli
- compile
- lang
- config
- emitter
- file
- log
- project
- uri
- util
- applyMatches
- base64
- camelcase
- clone
- copy
- del
- download
- escapeReg
- escapeShellArg
- escapeShellCmd
- exist
- ext
- filter
- find
- getMimeType
- glob
- install
- isAbsolute
- isDir
- isEmpty
- isFile
- isImageFile
- isTextFile
- isUtf8
- isWin
- map
- md5
- merge
- mkdir
- mtime
- nohup
- pad
- parseUrl
- pathinfo
- pipe
- query
- read
- readBuffer
- readJson
- realpath
- realpathSafe
- stringQuote
- toEncoding
- touch
- upload
- write