front-build
Advanced tools
Comparing version 0.3.2 to 0.3.3
## faq | ||
### 1. 页头页尾的 JS 和 CSS 应该放在哪里? | ||
可放在common里面,在页面引用。 | ||
### 2. common 和 utils 的区别? | ||
common 应直接在页面里面引用, utils需打包入page使用。 |
234
lib/app.js
@@ -6,2 +6,3 @@ var _ = require('underscore'); | ||
var async = require('async'); | ||
var pkginfo = require('../package.json'); | ||
@@ -18,2 +19,3 @@ var Page = require('./page') | ||
self.workDir = cfg.workDir || ''; | ||
self._loadPlugins(); | ||
}; | ||
@@ -24,9 +26,10 @@ | ||
PageDirs: ['core', 'mods', 'test'], | ||
version: '0.03', | ||
FB_JSON_NAME: 'fb.json', | ||
versionReg: /^\d+\.\d+$/, | ||
timestampReg: /^\d{6+}$/, | ||
version: pkginfo.version, | ||
defaultConfig: { | ||
version: '0.03', | ||
charset: 'utf8' | ||
//charset of common and utils | ||
charset: 'utf8', | ||
groups: {} | ||
}, | ||
@@ -50,3 +53,3 @@ | ||
if (app) { | ||
return callback(new Error('Already initd as a FB project;')); | ||
return callback(new Error('Already initd as a FB project')); | ||
} | ||
@@ -60,14 +63,3 @@ | ||
function (callback) { | ||
//create directories | ||
async.forEach(App.RootDirs, function (p, callback) { | ||
var tPath = path.resolve(dir, p) | ||
path.exists(tPath, function (exist) { | ||
if (exist) { | ||
console.log('directory exist: %s', p); | ||
return callback(null); | ||
} | ||
console.log('directory created: %s', p); | ||
fs.mkdir(tPath, callback); | ||
}); | ||
}, callback); | ||
app._initDirs(callback); | ||
}, | ||
@@ -83,2 +75,4 @@ | ||
], callback); | ||
}); | ||
@@ -88,2 +82,4 @@ | ||
/** | ||
@@ -105,3 +101,3 @@ * find the app rootDir by the fb.json file | ||
try { | ||
stat = fs.statSync(path.resolve(currentPath,'fb.json'));; | ||
stat = fs.statSync(path.resolve(currentPath, App.FB_JSON_NAME));; | ||
@@ -128,20 +124,20 @@ if (stat && stat.isFile) { | ||
try { | ||
var config = fu.readJSONSync(path.resolve(rootdir, 'fb.json')); | ||
} catch (e) { | ||
return callback(e); | ||
} | ||
fu.readJSON(path.resolve(rootdir, App.FB_JSON_NAME), function(err, json){ | ||
if (err) { | ||
console.log('%s 文件JSON格式错误或文件不存在。', App.FB_JSON_NAME); | ||
return callback(err); | ||
} | ||
var app = new App({ | ||
rootDir: rootdir, | ||
workDir: path.relative(rootdir, workDir) | ||
}); | ||
var app = new App({ | ||
rootDir: rootdir, | ||
workDir: path.relative(rootdir, workDir) | ||
}); | ||
app.getConfig(function(err, json) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
app.getConfig(function(err, json) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, app); | ||
callback(null, app); | ||
}); | ||
}); | ||
@@ -153,2 +149,44 @@ } | ||
_.extend(App.prototype, { | ||
_initDirs: function(callback){ | ||
//create directories | ||
var self = this; | ||
async.forEach(App.RootDirs, function (p, callback) { | ||
var tPath = path.resolve(self.rootDir, p); | ||
path.exists(tPath, function (exist) { | ||
if (exist) { | ||
console.log('directory exist: %s', p); | ||
return callback(null); | ||
} | ||
console.log('directory created: %s', p); | ||
fs.mkdir(tPath, callback); | ||
}); | ||
}, callback); | ||
}, | ||
update: function(dir, callback) { | ||
var self = this; | ||
async.series([ | ||
function (callback) { | ||
self._initDirs(callback); | ||
}, | ||
function (callback){ | ||
self.getConfig(callback); | ||
}, | ||
function (callback) { | ||
if (!self.config.fbversion || self.config.fbversion < App.version) { | ||
self.config.fbversion = App.version; | ||
self.config = _.extend(App.defaultConfig, self.config); | ||
self.saveConfig(callback); | ||
} else { | ||
callback(null); | ||
} | ||
} | ||
], callback); | ||
}, | ||
/** | ||
@@ -229,2 +267,7 @@ * Add page to app | ||
}, | ||
getCommonDir: function(){ | ||
var self = this; | ||
return path.resolve(self.rootDir, 'common'); | ||
}, | ||
/** | ||
@@ -235,7 +278,126 @@ * build the common | ||
buildCommon: function (callback){ | ||
console.log('TODO: build common'); | ||
callback(); | ||
var self = this; | ||
var commonDir = self.getCommonDir(); | ||
var src_temp_dir = path.resolve(self.rootDir, 'common_temp_src') | ||
var build_temp_dir = path.resolve(self.rootDir, 'common_temp_build'); | ||
self.getConfig(function(err, config) { | ||
if(err) { | ||
return callback(err); | ||
} | ||
var commonPage = { | ||
srcDir: src_temp_dir, | ||
destDir: build_temp_dir, | ||
inputCharset: config.charset, | ||
outputCharset: config.charset, | ||
config: config.common || {} | ||
}; | ||
self._buildCommon(commonPage, callback); | ||
}); | ||
}, | ||
_buildCommon: function(commonPage, callback){ | ||
var self = this; | ||
var commonDir = self.getCommonDir(); | ||
async.series([ | ||
function (callback){ | ||
//准备临时目录 | ||
// console.log('buildCommon: %s', 'prepare'); | ||
async.forEach([commonPage.srcDir, commonPage.destDir], function (absdir, callback){ | ||
path.exists(absdir, function(exist) { | ||
if (exist) { | ||
fu.rmTreeSync(absdir); | ||
} | ||
fs.mkdir(absdir, callback); | ||
}); | ||
}, callback); | ||
}, | ||
function (callback) { | ||
// console.log('buildCommon: %s', 'ICONV'); | ||
//编码转换 | ||
fu.iconv({ | ||
from: { | ||
path: commonDir, | ||
charset: self.config.charset, | ||
excludes: [ /-min\.\w+$/ ] | ||
}, | ||
to: { | ||
path: commonPage.srcDir, | ||
charset: 'utf8' | ||
} | ||
}, callback); | ||
}, | ||
function (callback) { | ||
// console.log('buildCommon: %s', 'plugins'); | ||
async.forEachSeries(self._plugins, function(plugin, callback) { | ||
plugin(commonPage, callback); | ||
}, callback); | ||
}, | ||
function (callback) { | ||
// console.log('buildCommon: %s', 'iconv'); | ||
//编码转换 | ||
fu.iconv({ | ||
from: { | ||
path: commonPage.destDir, | ||
charset: 'utf8', | ||
test: /-min\.\w+$/i | ||
}, | ||
to: { | ||
path: commonDir, | ||
charset: commonPage.outputCharset | ||
} | ||
}, callback); | ||
}, | ||
function (callback) { | ||
// console.log('buildCommon: %s', 'rmTemp'); | ||
fu.rmTreeSync(commonPage.srcDir); | ||
fu.rmTreeSync(commonPage.destDir); | ||
callback(null); | ||
} | ||
], callback); | ||
}, | ||
_loadPlugins: function() { | ||
var self = this; | ||
self.use(require('./plugins/module-compiler-common')()); | ||
self.use(require('./plugins/css-combo')({ | ||
includes: /-index\.css/i | ||
})); | ||
self.use(require('./plugins/lesscss')({ | ||
includes: /-index.less$/i | ||
})); | ||
self.use(require('./plugins/uglifyjs')()); | ||
self.use(require('./plugins/cssmin')()); | ||
}, | ||
/** | ||
* add plugin to Page | ||
* @param {Object} plugin the Page Plugins | ||
* @return {[type]} [description] | ||
*/ | ||
use: function(plugin) { | ||
var self = this; | ||
if (typeof plugin !== 'function') { | ||
return; | ||
} | ||
if (!self._plugins) { | ||
self._plugins = []; | ||
} | ||
self._plugins.push(plugin); | ||
}, | ||
/** | ||
* get config | ||
@@ -266,3 +428,5 @@ */ | ||
if (!self.config) { | ||
self.config = App.defaultConfig | ||
self.config = _.defaults({ | ||
fbversion: App.version | ||
}, App.defaultConfig); | ||
} | ||
@@ -269,0 +433,0 @@ |
@@ -0,7 +1,86 @@ | ||
var npminfo = require('../package.json'); | ||
var help_content = { | ||
'fb-init': { | ||
title: 'Init current directory as fb project', | ||
usage: 'fb init' | ||
}, | ||
'fb-add': { | ||
title: 'Create a page to current fb project', | ||
usage: 'fb add <pagename>' | ||
}, | ||
'fb-version': { | ||
title: 'Add a new version to current page', | ||
usage: 'fb version <versionValue>' | ||
}, | ||
'fb-build': { | ||
title: 'Build Page or Common', | ||
usage: [ | ||
'fb build <pageNameVersion>[ <pageNameVersion>...]', | ||
' [-t|-timestamp <timestamp>]', | ||
' [-v|-version <version>]', | ||
'fb build common' | ||
] | ||
}, | ||
'fb-group': { | ||
title: 'Group is design for build multi-pages at one time', | ||
usage: [ | ||
'fb group build <groupName>', | ||
'fb group[ list|ls]', | ||
'fb group set <groupName> <pageNameVersion>[ <pageNameVersion>...]', | ||
'fb group get <groupName>', | ||
'fb group rm <groupName>' | ||
] | ||
}, | ||
'fb-update': { | ||
title: 'update this fb project to current version', | ||
usage: [ | ||
'fb update' | ||
] | ||
} | ||
} | ||
function printLine (content) { | ||
console.log('\t%s', content); | ||
} | ||
function printHelp(name) { | ||
var help; | ||
if (help_content[name]) { | ||
help = help_content[name]; | ||
console.log('NAME'); | ||
console.log('\t%s - %s', name, help.title); | ||
console.log('SYNOPSIS'); | ||
if (typeof help.usage === 'object') { | ||
help.usage.forEach(function(line) { | ||
printLine(line); | ||
}); | ||
} else { | ||
printLine(help.usage); | ||
} | ||
} | ||
} | ||
module.exports = function (bin, callback) { | ||
var argv = bin.argv; | ||
var cwd = bin.cwd; | ||
console.log('FrontBuild(fb): a front build framework;'); | ||
console.log('commands:'); | ||
console.log ('init, add, version, build, group, help'); | ||
if (argv._.length < 2) { | ||
console.log('FrontBuild(fb): front end build framework'); | ||
console.log('version: %s', npminfo.version); | ||
console.log('commands:'); | ||
console.log ('\tinit, add, version, build, group, help, update'); | ||
console.log('options:'); | ||
console.log ('\t--debug: Turn on debug mode'); | ||
callback(null, ''); | ||
} | ||
if (argv._.length >= 2) { | ||
var content = argv._[1]; | ||
printHelp('fb-' + content); | ||
} | ||
} |
@@ -22,7 +22,44 @@ var fs = require('fs'); | ||
App.init(target, callback); | ||
App.init(target, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
}, | ||
update: function(bin, callback) { | ||
var argv = bin.argv; | ||
App.getApp(bin.cwd, function(err, app){ | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (!app) { | ||
console.error('不在FB项目,请先初始化!'); | ||
return callback(new Error('build fail, not a fb app;')); | ||
} | ||
if (app.config.fbversion || app.config.version) { | ||
console.log('fb app version was %s', app.config.fbversion || app.config.version ); | ||
} | ||
if (app.rootDir != bin.cwd) { | ||
console.error('请在FB项目根目录执行此命令'); | ||
return callback(new Error('not fb root')); | ||
} | ||
app.update(function(err){ | ||
if (err) { | ||
return callback(err); | ||
} | ||
console.log('update success! current app version is %s', app.config.fbversion); | ||
}); | ||
}); | ||
}, | ||
/** | ||
* fb build pagenname@pageversion -t 1000 | ||
* fb build pagenname@pageversion -t 20120555 | ||
*/ | ||
@@ -42,3 +79,3 @@ | ||
if (!app) { | ||
App.error('不在FB项目,请先初始化!'); | ||
console.error('不在FB项目,请先初始化!'); | ||
return callback(new Error('build fail, not a fb app;')); | ||
@@ -106,3 +143,8 @@ } | ||
page.build(job.timestamp, callback); | ||
}, callback); | ||
}, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
}); | ||
@@ -131,3 +173,8 @@ | ||
page = app.getPage(app.getCurrent().pageName); | ||
page.addVersion(version, callback); | ||
page.addVersion(version, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
}); | ||
@@ -148,3 +195,8 @@ }, | ||
} | ||
app.addPage(argv._[1], callback); | ||
app.addPage(argv._[1], function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
}); | ||
@@ -164,2 +216,6 @@ }, | ||
if (!app) { | ||
return callback(new Error('not in a fb project!')) | ||
} | ||
var command = argv._[1] || 'list'; | ||
@@ -239,6 +295,31 @@ | ||
console.log('%s: %s', groupName, config.groups[groupName].join(', ')); | ||
app.saveConfig(callback); | ||
app.saveConfig(function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
}); | ||
break; | ||
case 'get': | ||
case 'show': | ||
groupName = argv._[2]; | ||
app.getGroup(groupName, function (err, pages) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (!pages || pages.length === 0) { | ||
return callback(new Error('no group')); | ||
} | ||
pages.forEach(function (page) { | ||
console.log(page); | ||
}); | ||
callback(null); | ||
}); | ||
break; | ||
case 'rm': | ||
@@ -249,3 +330,8 @@ case 'del': | ||
groupName = argv._[2]; | ||
app.rmGroup(groupName, callback); | ||
app.rmGroup(groupName, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
break; | ||
@@ -269,6 +355,10 @@ case 'build': | ||
app.buildGroup(groupName, timestamp, callback); | ||
app.buildGroup(groupName, timestamp, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(null, 'success!'); | ||
}); | ||
break; | ||
} | ||
@@ -275,0 +365,0 @@ |
@@ -49,3 +49,6 @@ var path = require('path'); | ||
.filter(function(p){ | ||
return reg.test(p); | ||
if(reg instanceof RegExp) { | ||
return reg.test(p); | ||
} | ||
return true; | ||
}) | ||
@@ -61,2 +64,36 @@ .value(); | ||
/** | ||
* 创建目录, 创建父目录如果不存在 | ||
* @param {string} p path of dir to create | ||
* * @param {string} mode path of dir to create | ||
* @param {Function} callback call with (error) | ||
*/ | ||
var mkdirp = exports.mkdirp = require('mkdirp'); | ||
// function(p, mode, callback) { | ||
// if(typeof mode === 'function') { | ||
// callback = mode; | ||
// mode = '0777'; | ||
// } | ||
// var p = path.resolve(p); | ||
// var currentDir = p; | ||
// var ar_p = []; | ||
// while (!path.existsSync(currentDir)) { | ||
// ar_p.unshift(currentDir); | ||
// currentDir = path.dirname(currentDir); | ||
// }; | ||
// if(ar_p.length === 0) { | ||
// return callback(null); | ||
// } | ||
// async.forEach(ar_p, function(dir, callback){ | ||
// process.nextTick(function(){ | ||
// fs.mkdir(dir, mode, callback); | ||
// }); | ||
// }, callback); | ||
// }; | ||
/** | ||
* copy the entire directory | ||
@@ -185,3 +222,2 @@ * @param {String} src_path path of the directory copy from | ||
try { | ||
json = JSON.parse(text); | ||
@@ -252,1 +288,61 @@ } catch (e) { | ||
}; | ||
exports.iconv = function(config, callback){ | ||
var src_path = path.resolve(config.from.path); | ||
var to_path = path.resolve(config.to.path); | ||
var from_charset = config.from.charset; | ||
var to_charset = config.to.charset; | ||
var reg = config.from.test || null; | ||
var excludes = config.from.excludes; | ||
if (!src_path || !to_path || !from_charset || !to_charset) { | ||
return callback(new Error('not enough params')); | ||
} | ||
findInDir(src_path, reg, function (err, paths) { | ||
if(err) { | ||
return callback(err); | ||
} | ||
var paths = paths.filter(function (p) { | ||
if (excludes){ | ||
for(var i =0; i < excludes.length; i++ ) { | ||
var reg = excludes[i]; | ||
if (reg.test && reg.test(p)) { | ||
return false; | ||
} | ||
if (typeof reg === 'string' && reg === path.filename(p) ) { | ||
return false; | ||
} | ||
} | ||
} | ||
return true; | ||
}); | ||
if(paths.length === 0) { | ||
return callback(null); | ||
} | ||
async.forEach(paths, function (filepath, callback) { | ||
filepath = path.resolve(src_path, filepath); | ||
var srt; | ||
var rel = path.relative(src_path, filepath); | ||
var target = path.resolve(to_path, rel); | ||
var buf = fs.readFileSync(filepath); | ||
if (from_charset != to_charset) { | ||
srt = iconv.decode(buf, from_charset); | ||
buf = iconv.encode(srt, to_charset); | ||
} | ||
mkdirp(path.dirname(target), function(err) { | ||
if(err) { | ||
return callback(err); | ||
} | ||
fs.writeFile(target, buf, callback); | ||
}); | ||
}, callback); | ||
}); | ||
} |
@@ -56,3 +56,3 @@ var _ = require('underscore'); | ||
} | ||
return obj; | ||
@@ -101,9 +101,3 @@ } | ||
//mkdir s | ||
async.forEach( | ||
Page.DIRS, | ||
function (name, callback){ | ||
fs.mkdir(path.resolve(versionDir, name), callback) | ||
}, | ||
callback | ||
); | ||
self._initDirs(versionDir, callback); | ||
}, | ||
@@ -156,5 +150,6 @@ | ||
fu.readJSON(path.resolve(self.versionDir, Page.JSON_NAME), function (err, json) { | ||
if (!err && json) { | ||
_.extend(self.config, json); | ||
if (err) { | ||
return callback(err); | ||
} | ||
_.extend(self.config, json); | ||
@@ -168,12 +163,43 @@ self.input_charset = self.config.inputCharset || self.charset; | ||
}, | ||
/** | ||
* update prev version of fb version to current; | ||
* @return {[type]} [description] | ||
*/ | ||
updateVersion: function(callback) { | ||
//TODO | ||
}, | ||
_initDirs: function(versionDir, callback) { | ||
async.forEach( | ||
Page.DIRS, | ||
function (name, callback){ | ||
fs.mkdir(path.resolve(versionDir, name), callback) | ||
}, | ||
callback | ||
); | ||
}, | ||
_loadPlugins: function(callback) { | ||
var self = this; | ||
self.use(require('./plugins/module-compiler')()); | ||
self.use(require('./plugins/css-combo')()); | ||
self.use(require('./plugins/lesscss')()); | ||
self.use(require('./plugins/module-compiler')({ | ||
base: 'core' | ||
})); | ||
self.use(require('./plugins/css-combo')({ | ||
base: 'core' | ||
})); | ||
self.use(require('./plugins/lesscss')({ | ||
base: 'core' | ||
})); | ||
self.use(require('./plugins/concat')()); | ||
self.use(require('./plugins/uglifyjs')()); | ||
self.use(require('./plugins/cssmin')()); | ||
self.use(require('./plugins/uglifyjs')({ | ||
base: 'core' | ||
})); | ||
self.use(require('./plugins/cssmin')({ | ||
base: 'core' | ||
})); | ||
@@ -185,2 +211,3 @@ callback(null); | ||
var self = this; | ||
if (!self.version) { | ||
@@ -303,2 +330,5 @@ return callback(new Error('Page#build: version is not setted; ')); | ||
} | ||
if (!self._plugins) { | ||
self._plugins = []; | ||
} | ||
@@ -305,0 +335,0 @@ self._plugins.push(plugin); |
@@ -13,5 +13,14 @@ /** | ||
var lineEnds = { | ||
dos: '\r\n', | ||
unix: '\n', | ||
mac: '\r' | ||
}; | ||
function doConcat (job, callback) { | ||
var target = job.output; | ||
var files = job.files; | ||
var lineEnd = job.lineEnd || '\n'; | ||
var lineEndBuf = new Buffer(lineEnd); | ||
@@ -26,10 +35,16 @@ if(!target || !files || !files.length) { | ||
}); | ||
var index = 0; | ||
async.forEachSeries( | ||
files, | ||
files, | ||
function(file, callback){ | ||
index += 1; | ||
fs.readFile(file, function(err, data){ | ||
var rs = fs.createReadStream(file); | ||
rs.on('end', callback); | ||
// write a LineEnd | ||
rs.on('end', function (ev) { | ||
ws.once('drain', callback); | ||
ws.write(lineEndBuf); | ||
}); | ||
rs.pipe(ws, {end: false}); | ||
@@ -55,2 +70,6 @@ }); | ||
var config = page.config; | ||
var fileFormat = config.fileFormat || 'unix'; | ||
var lineEnd = lineEnds[fileFormat]; | ||
var jobs = []; | ||
@@ -86,3 +105,4 @@ if (!config || !config.concat || !config.concat.length === 0) { | ||
output: path.resolve(page.destDir, to), | ||
files: list | ||
files: list, | ||
lineEnd: lineEnd | ||
}); | ||
@@ -89,0 +109,0 @@ }); |
var CssCombo = require('css-combo'); | ||
var fu = require('../fileutil'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var async = require('async'); | ||
var _ = require('underscore') | ||
/** | ||
@@ -9,34 +11,78 @@ * plugin css-combo for FrontBuild | ||
*/ | ||
module.exports = function (){ | ||
var cssReg = /.*\.css$/i; | ||
module.exports = function (config){ | ||
config = _.extend({ | ||
//css 正则 | ||
cssReg: /.*\.css$/i, | ||
// 子目录 | ||
base: '.', | ||
compress: 0 | ||
}, config); | ||
return function (page, next) { | ||
console.log('plugin: css-combo') | ||
var srcCore = path.resolve(page.srcDir, 'core'); | ||
var buildCore = path.resolve(page.destDir, 'core'); | ||
path.exists(srcCore, function (exist) { | ||
var srcBase = path.resolve(page.srcDir, config.base); | ||
var buildBase = path.resolve(page.destDir, config.base); | ||
path.exists(srcBase, function (exist) { | ||
if (!exist) { | ||
return next(); | ||
} | ||
fu.findInDir(srcCore, cssReg, function(err, files){ | ||
fs.readdir(srcBase, function(err, files) { | ||
if (err) { | ||
return next(err); | ||
} | ||
if (!files.length) { | ||
return next(); | ||
} | ||
async.forEach(files, function (cssfile, callback) { | ||
async.filter( | ||
files, | ||
function(file, callback) { | ||
CssCombo.build({ | ||
target: path.resolve(srcCore, cssfile), | ||
inputEncoding: page.charset, | ||
outputEncoding: page.charseet, | ||
debug: page.debug, | ||
output: path.resolve(buildCore) | ||
}, callback); | ||
fs.stat(path.resolve(srcBase, file), function(err, stat){ | ||
}, next); | ||
if(err) { | ||
return callback(false); | ||
} | ||
if (!stat.isFile()) { | ||
return callback(false); | ||
} | ||
if (!/\.css$/i.test(file)) { | ||
return callback(false); | ||
} | ||
if (/-min\.css$/i.test(file)) { | ||
return callback(false); | ||
} | ||
return callback(true); | ||
}); | ||
}, | ||
function (files) { | ||
if (files.length === 0) { | ||
return next(null); | ||
} | ||
async.forEach(files, function (file, callback) { | ||
var src = path.resolve(srcBase, file); | ||
var output = path.resolve(buildBase, file.replace(/\.css/i, '-min.css')); | ||
CssCombo.build({ | ||
target: path.resolve(srcBase, file), | ||
inputEncoding: page.charset, | ||
outputEncoding: page.charseet, | ||
debug: page.debug, | ||
output: path.resolve(buildBase, file), | ||
compress: config.compress | ||
}, callback); | ||
}, next); | ||
}); | ||
}); | ||
}); | ||
} | ||
}; |
@@ -6,2 +6,3 @@ var compressor = require('../cssmin').compressor; | ||
var async = require('async'); | ||
var _ = require('underscore'); | ||
@@ -43,11 +44,15 @@ | ||
module.exports = function(){ | ||
module.exports = function(config) { | ||
_.extend(config); | ||
return function (page, next) { | ||
console.log('plugin: cssmin') | ||
var build_core = path.resolve(page.destDir, 'core'); | ||
path.exists(build_core, function (exist) { | ||
console.log('plugin: cssmin'); | ||
var destDir = path.resolve(page.destDir); | ||
path.exists(destDir, function (exist) { | ||
if (!exist) { | ||
return next(); | ||
} | ||
build_dir(build_core, next); | ||
build_dir(destDir, next); | ||
}); | ||
@@ -54,0 +59,0 @@ |
@@ -14,5 +14,11 @@ /** | ||
var async = require('async'); | ||
var _ = require('underscore'); | ||
var parseLessFile = function (target, options, callback) { | ||
var parser = new(less.Parser)(options); | ||
var parser = new(less.Parser)({ | ||
paths: options.paths, | ||
optimization: options.optimization, | ||
filename: options.filename, | ||
strictImports: options.strictImports | ||
}); | ||
@@ -28,4 +34,6 @@ parser.parse(target.data, function (err, tree) { | ||
var css = tree.toCSS({ | ||
compress: false | ||
compress: options.compress, | ||
yuicompress: options.yuicompress | ||
}); | ||
fs.writeFile(target.dest, css, 'utf8', callback); | ||
@@ -41,41 +49,69 @@ | ||
module.exports = function(config) { | ||
var config = config || {}; | ||
var config = _.defaults(config, { | ||
base: '', | ||
compress: false, | ||
yuicompress: false, | ||
optimization: 1, | ||
silent: false, | ||
color: true, | ||
strictImports: false | ||
}); | ||
return function (page, next) { | ||
console.log('plugin: less'); | ||
var core_src = path.resolve(page.srcDir, 'core'); | ||
var core_build = path.resolve(page.destDir, 'core'); | ||
fu.findInDir(core_src, /.*\.less$/, function(err, files) { | ||
console.log('plugin: lesscss') | ||
var base_src = path.resolve(page.srcDir, config.base); | ||
var base_build = path.resolve(page.destDir, config.base); | ||
fs.readdir(base_src, function(err, files) { | ||
if (err) { | ||
return next(err); | ||
} | ||
async.filter( | ||
files, | ||
function(file, callback) { | ||
fs.stat(path.resolve(base_src, file), function(err, stat){ | ||
if(err) { | ||
return callback(err); | ||
} | ||
callback(stat.isFile() && /\.less$/i.test(file)); | ||
}); | ||
}, | ||
function (files) { | ||
if (!files || files.length === 0) { | ||
return next(); | ||
} | ||
if (files.length === 0) { | ||
return next(null); | ||
} | ||
async.forEach(files, function (file, callback){ | ||
var src = path.resolve(core_src, file); | ||
var dest = path.resolve(core_build, file + '.css'); | ||
fs.readFile(src, 'utf8', function(err, data) { | ||
async.forEach(files, function (file, callback){ | ||
var src = path.resolve(base_src, file); | ||
if (err) { | ||
return callback(err); | ||
} | ||
var dest = path.resolve(base_build, file.replace(/\.less$/i, '.css')); | ||
parseLessFile({ | ||
data: data, | ||
dest: dest | ||
}, { | ||
yuicompress: false, | ||
optimization: 1, | ||
silent: false, | ||
paths: [ path.dirname(src) ], | ||
color: true, | ||
filename: file, | ||
strictImports: false | ||
}, callback); | ||
}); | ||
}, next); | ||
fs.readFile(src, 'utf8', function(err, data) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
parseLessFile({ | ||
data: data, | ||
dest: dest | ||
}, { | ||
paths: [ path.dirname(src) ], | ||
compress: config.compress, | ||
yuicompress: config.yuicompress, | ||
optimization: config.optimization, | ||
silent: config.silent, | ||
color: config.color, | ||
filename: file, | ||
strictImports: config.strictImports | ||
}, callback); | ||
}); | ||
}, next); | ||
}); | ||
}); | ||
} | ||
} |
var ModuleComplier = require('tbuild').ModuleComplier; | ||
var path = require('path'); | ||
var _ = require('underscore'); | ||
module.exports = function () { | ||
module.exports = function (config) { | ||
config = config || {}; | ||
config = _.defaults(config, { | ||
base: '' | ||
}); | ||
@@ -11,4 +17,3 @@ return function (page, next) { | ||
path: page.srcDir, | ||
charset: page.charset, | ||
suffix: 1 | ||
charset: page.charset | ||
}, | ||
@@ -28,3 +33,4 @@ { | ||
}); | ||
ModuleComplier.build(path.resolve(page.srcDir, 'core'), path.resolve(page.destDir, 'core')); | ||
ModuleComplier.build(path.resolve(page.srcDir, config.base), path.resolve(page.destDir, config.base)); | ||
} catch (e) { | ||
@@ -31,0 +37,0 @@ return callback(e); |
@@ -19,2 +19,3 @@ var fs = require('fs'); | ||
var compressed = pro.gen_code(ast, gen_config); | ||
compressed += '; '; | ||
fs.writeFile(to_file, compressed, done); | ||
@@ -32,7 +33,6 @@ }); | ||
module.exports = function (config) { | ||
config = config || {}; | ||
config = _.defaults(config, { | ||
config = _.extend({ | ||
ascii_only: true | ||
}); | ||
}, config); | ||
@@ -39,0 +39,0 @@ var options = { |
@@ -5,3 +5,3 @@ { | ||
"description": "build front project", | ||
"version": "0.3.2", | ||
"version": "0.3.3", | ||
"repository": { | ||
@@ -29,3 +29,4 @@ "type": "git", | ||
"underscore": "~1.3.3", | ||
"css-combo": "~0.1.8" | ||
"css-combo": "~0.1.8", | ||
"mkdirp": "~0.3.3" | ||
}, | ||
@@ -35,2 +36,2 @@ "devDependencies": { | ||
} | ||
} | ||
} |
# Front-Build | ||
- 基于目录规范 | ||
- 自动化打包 | ||
- 零配置 | ||
- 基于目录规范 | ||
- 自动化打包,追求零配置 | ||
- 面向前端 | ||
## 目录规范 | ||
@@ -42,3 +43,3 @@ | ||
│ └ style2-min.css | ||
└ fb.json // 应用的配置 | ||
└ fb.json // 应用的配置, fb 应用根路径的标识 | ||
```` | ||
@@ -48,10 +49,12 @@ | ||
### page | ||
### 应用的 page 构建 | ||
#### 特点 | ||
- 基于时间戳目录 | ||
- 从入口文件(core 目录)开始 | ||
- 支持编码设置 | ||
#### fb使用以下步骤构建 | ||
#### fb使用以下步骤构建 page | ||
特点 | ||
- 发布基于时间戳目录 | ||
- core 目录是编译入口 | ||
- 开发环境与生产环境灵活切换 | ||
1. 创建目录 临时src (page.srcDir); 临时build (page.destDir); timestame目录 | ||
@@ -68,3 +71,3 @@ 2. 将版本目录里面的文件,转成utf8编码, 并全部拷贝到 src 目录 | ||
4. 将build下的所有文件转码到outpuCharse,并复制到timestamp目录 | ||
4. 将build下的所有文件转码到outputCharset,并复制到timestamp目录 | ||
5. 在timestamp 目录下生成 包含打包信息的 build.json. | ||
@@ -74,24 +77,33 @@ | ||
### 应用的 common 使用以下步骤构建 | ||
### 应用的 common 构建 | ||
特点 | ||
- common 根目录下的文件为打包入口 | ||
- 可在 fb.json 里面配置文件编码 | ||
1. 创建目录: 临时src (common.srcDir); 临时build (common.destDir); | ||
2. 将common目录里面源码文件,转成utf8编码, 并全部拷贝到 src 目录 | ||
2. 将common目录里面源码文件,从 inputCharset 转成utf8编码, 并全部拷贝到 临时src 目录 | ||
3. 使用内置插件系统 | ||
1. module-compiler: KISSY的模块打包压缩, 从src/xx.index.js -> build/xx.index-minjs | ||
2. lesscss: 打包, 从 src/xx.less -> build/core/xx.less.css | ||
3. uglifyjs: build/xx.js -> build/xx-min.js | ||
4. cssmin: build/xx.css -> build/xx-min.css | ||
1. module-compiler: KISSY的模块打包压缩, 从src/*xx.js* -> build/*xx-min.js* | ||
2. lesscss: 打包, 从 src/*xx.less* -> build/*xx-min.css* | ||
3. uglifyjs: *build*/*xx.js* -> build/*xx-min.js* | ||
4. cssmin: *build*/*xx.css* -> build/*xx-min.css* | ||
4. 将最终压缩文件 build/xx-min.yy 文件从 utf-8 转码到 outpuCharse,并复制回 common | ||
5. 在timestamp 目录下生成 包含打包信息的 build.json. | ||
4. 将临时build 目录下的 **-min.** 等压缩文件从 utf-8 转码到 outputCharset,并复制回 common目录 | ||
#### 注意点 | ||
- common 目录的 outputCharset === inputCharset, 可在 fb.json 里面配置 charset | ||
- 可在app 里面执行 fb build common | ||
### utils | ||
TODO | ||
### utils 构建 | ||
utils 一般不直接使用, 可打包进page。 | ||
## 快速开始 | ||
### 安装 | ||
1. 首先安装nodejs环境 (and npm) http://nodejs.org/#download; | ||
@@ -101,3 +113,8 @@ 2. npm install front-build -g; | ||
### 更新 | ||
1. npm update front-build -g | ||
2. done! | ||
````sh | ||
@@ -112,2 +129,12 @@ cd dir/to/app | ||
````sh | ||
cd dir/to/app | ||
fb update | ||
```` | ||
更新fb项目到最新版本 | ||
此命令会更新fb.json, 在其中添加新版本的一些配置信息。 | ||
同时会创建缺失的文件夹。 | ||
````sh | ||
fb add name_of_page | ||
@@ -132,3 +159,3 @@ ```` | ||
构建 1.0(Version) 的 about(Page) 到时间戳目录 ‘20120601’ | ||
构建 1.0(Version) 的 about(当前PageName) 到时间戳目录 ‘20120601’ | ||
@@ -165,2 +192,2 @@ | ||
* linux | ||
* OSX | ||
* OSX 10.7 + |
{ | ||
"version": "0.03", | ||
"charset": "utf8", | ||
"groups": { | ||
@@ -11,3 +11,4 @@ "all": [ | ||
] | ||
} | ||
}, | ||
"fbversion": "0.3.2" | ||
} |
KISSY.add(function(){ | ||
var a = 'mods:mod1.js'; | ||
}, { | ||
requires: ['../mods/submod1.js'] | ||
}); | ||
requires: ['../mods/submod1.js', 'utils/sample/index']}); |
var fs = require('fs'); | ||
var path = require('path'); | ||
var should = require('should'); | ||
var async = require('async'); | ||
var fu = require('../lib/fileutil'); | ||
@@ -37,3 +37,3 @@ var App = require('../lib/app'); | ||
json.should.be.ok; | ||
should.exist(json.version); | ||
should.exist(json.fbversion); | ||
done(); | ||
@@ -262,2 +262,49 @@ }); | ||
}); | ||
}); | ||
describe('test app buildCommon', function(){ | ||
var app; | ||
var rootDir = path.resolve('sample-project'); | ||
var files = [ | ||
'index.js', | ||
'main.css', | ||
'style.less' | ||
]; | ||
var minFiles = [ | ||
'index-min.js', | ||
'main-min.css', | ||
'style-min.css' | ||
]; | ||
before(function (done) { | ||
app = new App({ | ||
rootDir: rootDir | ||
}); | ||
app.buildCommon(done); | ||
}); | ||
after(function (done){ | ||
async.forEach(minFiles, function (file, callback){ | ||
fs.unlink(path.resolve(rootDir, 'common', file), callback); | ||
}, done); | ||
}); | ||
it('should build files to -min', function(done) { | ||
async.map(minFiles, function (file, callback) { | ||
fs.stat(path.resolve(rootDir, 'common', file), callback); | ||
}, function (err, stats){ | ||
if (err) { | ||
return done(err); | ||
} | ||
stats.forEach(function (stat) { | ||
stat.isFile().should.be.true; | ||
}); | ||
done(); | ||
}) | ||
}); | ||
}); |
@@ -76,2 +76,3 @@ var fs = require('fs'), | ||
} | ||
fu.writeJSON(json_file_name, obj, done); | ||
@@ -185,3 +186,2 @@ }) | ||
list.length.should.eql(4); | ||
console.log(list); | ||
list.indexOf(path.join('sub2', 'find.js')).should.not.eql(-1); | ||
@@ -194,3 +194,21 @@ done(); | ||
describe('test mkdirp', function() { | ||
var pathtocreate = './test-mkdirp/path/from/here'; | ||
before(function(done){ | ||
fu.mkdirp(pathtocreate, done); | ||
}); | ||
after(function(){ | ||
fu.rmTreeSync('./test-mkdirp'); | ||
}); | ||
it('should create the target direcotry witout error', function(done){ | ||
path.exists(pathtocreate, function(exist){ | ||
exist.should.be.true; | ||
done(); | ||
}); | ||
}) | ||
}); | ||
describe('test iconv_copy_tree', function(){ | ||
@@ -211,2 +229,38 @@ var src = path.resolve('files/test_tree'); | ||
}); | ||
}); | ||
}); | ||
describe('test fileutil.iconv', function(){ | ||
var src = './files/test_tree'; | ||
var dst = './files/copy_of_test_iconv'; | ||
before(function(done){ | ||
var minReg = /-min\.\w+$/i; | ||
fu.iconv({ | ||
from: { | ||
path: src, | ||
charset: 'gbk', | ||
test: /\.txt$/i, | ||
excludes: [minReg] | ||
}, | ||
to: { | ||
path: dst, | ||
charset: 'utf8' | ||
} | ||
}, | ||
done); | ||
}); | ||
after(function(){ | ||
fu.rmTreeSync(dst); | ||
}); | ||
it('should conv files from gbk to utf8', function () { | ||
path.existsSync(path.resolve(dst, 'this_is_gbk')).should.be.false; | ||
var file2 = fs.readFileSync(path.resolve(dst, 'sub1/iconv.gbk.txt'), 'utf8'); | ||
file2.should.include('中文 2'); | ||
}); | ||
it('should not conv files that match excludes tests', function(){ | ||
var p = path.resolve(dst, 'sub1/iconv.gbk-min.txt'); | ||
path.existsSync(p).should.be.false; | ||
}); | ||
}); | ||
@@ -17,3 +17,3 @@ var Page = require('../lib/page'); | ||
var version = '1.0'; | ||
var timestamp = '20120505'; | ||
var timestamp = '20121212'; | ||
var thepage; | ||
@@ -41,3 +41,3 @@ | ||
after(function (done) { | ||
//fu.rmTreeSync(path.resolve(rootDir, timestamp)); | ||
fu.rmTreeSync(path.resolve(rootDir, timestamp)); | ||
done(); | ||
@@ -80,3 +80,3 @@ }); | ||
it('should build less', function(done) { | ||
var buildLessFile = path.resolve(rootDir, timestamp, 'core/index.less.css'); | ||
var buildLessFile = path.resolve(rootDir, timestamp, 'core/lessfile.css'); | ||
@@ -93,4 +93,22 @@ fs.readFile(buildLessFile, 'utf8', function(err, data) { | ||
it('should build kissy', function(done) { | ||
var buildjsfile = path.resolve(rootDir, timestamp, 'core/index.js'); | ||
fs.readFile(buildjsfile, 'utf8', function(err, data) { | ||
if (err) { | ||
return done(err); | ||
} | ||
data.should.include("KISSY.add('mods/mod1',"); | ||
data.should.include("KISSY.add('mods/mod2',"); | ||
data.should.include("KISSY.add('core/index',"); | ||
data.should.include("KISSY.add('mods/submod1',"); | ||
//utils | ||
data.should.include("utils-sample-index.js"); | ||
done(); | ||
}); | ||
}); | ||
it('should compress css to -min.css', function(done) { | ||
var minLessCss = path.resolve(rootDir, timestamp, 'core/index.less-min.css'); | ||
var minLessCss = path.resolve(rootDir, timestamp, 'core/lessfile-min.css'); | ||
var minIndexCss = path.resolve(rootDir, timestamp, 'core/index-min.css') | ||
@@ -143,3 +161,2 @@ | ||
var version = '2.0'; | ||
var timestamp = '20120505'; | ||
var thepage; | ||
@@ -146,0 +163,0 @@ |
Sorry, the diff of this file is not supported yet
110637
65
2939
185
9
12
+ Addedmkdirp@~0.3.3
+ Addedmkdirp@0.3.5(transitive)