Skip to content

Instantly share code, notes, and snippets.

@htom78
Forked from sorrycc/config.js
Last active August 29, 2015 14:19
Show Gist options
  • Save htom78/2f46f9e9ad2adea04826 to your computer and use it in GitHub Desktop.
Save htom78/2f46f9e9ad2adea04826 to your computer and use it in GitHub Desktop.
var join = require('path').join;
var DEFAULT_CONFIG = {
// spm 源
"registry": "http://private-spm-url",
// Rename files
// 详见:https://github.com/jeremyruppel/pathmap
"pathmap": null,
// JS 压缩配置项
// 详见:https://github.com/terinjokes/gulp-uglify/
"uglifyOpts": {
"output": {
"ascii_only": true
}
},
// CSS 压缩配置项
// 详见:https://github.com/GoalSmashers/clean-css
"cssminOpts": {
"noAdvanced": true
},
// less 编译配置项
// 详见:https://github.com/plus3network/gulp-less
"lessOpts": null,
// coffee 预编译配置项
// 详见:https://github.com/wearefractal/gulp-coffee
"coffeeOpts": {
"bare": true
},
// autoprefixer 配置项, 默认关闭
// https://github.com/sindresorhus/gulp-autoprefixer
"autoprefixer": false
};
//////////////////////
// Exports.
exports.get = function(cwd) {
cwd = cwd || process.cwd();
var userConfig = require(join(cwd, 'package.json')).spm;
return extendExist(DEFAULT_CONFIG, userConfig);
};
/////////////////////
// Helpers.
function extendExist(source, obj) {
var ret = {};
for (var k in source) {
ret[k] = source[k];
if (typeof obj[k] === 'object' && typeof source[k] === 'object') {
for (var kk in obj[k]) {
ret[k][kk] = obj[k][kk];
}
} else if(typeof obj[k] !== 'undefined') {
ret[k] = obj[k];
}
}
return ret;
}
'use strict';
require('colorful').colorful();
var fs = require('fs');
var path = require('path');
var umi = require('umi');
var join = require('path').join;
var glob = require('glob');
var uniq = require('array-uniq');
var spmrc = require('spmrc');
var log = require('spm-log');
var multipipe = require('multipipe');
var gulp = require('gulp');
var standalonify = require('spm-standalonify');
var through = require('through2');
var extend = require('extend');
var checkonline = require('checkonline');
var install = require('co')(require('spm-client').install);
var autoprefixer = require('gulp-autoprefixer');
var cssmin = require('gulp-cssmin');
var transport = require('gulp-transport');
var gulpif = require('gulp-if');
var mirror = require('gulp-mirror');
var pathmap = require('gulp-pathmap');
var clean = require('gulp-clean');
var less = require('gulp-less');
var coffee = require('gulp-coffee');
var uglify = require('gulp-uglify');
var slash = require('gulp-slash');
module.exports = function(opts, cb) {
var args = umi.buildArgs(opts);
args = extend(require('./config').get(args.cwd), args);
logArgs(args);
// load spmfile
var spmfile = join(args.cwd, 'spmfile.js');
args.spmfile = {};
if (fs.existsSync(spmfile)) {
args.spmfile = require(spmfile);
}
// define task
defineCleanTask(args);
defineInstallTask(args);
defineCustomTasks(args);
definePrecompileTasks(args);
defineBuildTask(args);
defineCheckOnlineTask(args);
gulp
.on('task_start', function(e) {showLog('start', e);})
.on('task_stop', function(e) {showLog('end', e);});
gulp.start('checkonline', cb);
};
//////////////////
// Tasks
function defineCheckOnlineTask(args) {
gulp.task('checkonline', ['endTask'], function(callback) {
var isRelative = args.include === 'relative';
var currPkg = args.pkg.name + '/' + args.pkg.version;
checkonline({
cwd: args.cwd,
server: 'https://a.alipayobjects.com',
statusCode: 404,
withDeps: isRelative
}, function(err, files) {
if (err) {
return callback(err);
}
// Check if dependencies pkgs is online
if (isRelative) {
var pkgs = files
// is not online and not current pkg
.filter(function(f) {return !f.isOnline && f.filepath.indexOf(currPkg) !== 0;})
// get pkg list in `name@version`
.map(function(f) {return f.filepath.split('/').slice(0,2).join('@')});
pkgs = uniq(pkgs);
if (pkgs.length) {
log.warn('checkonline', 'These deps are not published: ' + pkgs.join(', ').to.yellow.color);
}
}
if (files.filter(isCurrentPkgPublished).length) {
var errMsg = 'This version '+(args.pkg.name+'@'+args.pkg.version)+' is already published!';
return callback(new Error(errMsg));
}
callback();
function isCurrentPkgPublished(f) {
return f.filepath.indexOf(currPkg) === 0 && f.isOnline;
}
});
});
}
function defineCleanTask(args) {
gulp.task('clean', function() {
return args.force && gulp.src(args.dest)
.pipe(clean({force: true}));
});
}
function defineInstallTask(args) {
gulp.task('install', args.install ? function(callback) {
var opts = {
cwd: args.cwd,
registry: args.registry
};
install(opts, callback);
} : noop);
}
function defineCustomTasks(args) {
gulp.task('startTask', ['clean'],
args.spmfile.startTask ? args.spmfile.startTask(args) : function(){});
gulp.task('endTask', ['build'],
args.spmfile.endTask ? args.spmfile.endTask(args) : function(){});
}
function definePrecompileTasks(args) {
var lessFiles = getLessFiles(args);
gulp.task('less', ['startTask'], function() {
if (!lessFiles.length) return;
return pipe(
gulp.src(lessFiles, {cwd:args.cwd, base:args.cwd}),
less(args.lessOpts || {paths: [args.cwd]}),
gulp.dest(args.cwd)
);
});
var coffeeFiles = getCoffeeFiles(args);
gulp.task('coffee', ['startTask'], function() {
if (!coffeeFiles.length) return;
return pipe(
gulp.src(coffeeFiles, {cwd:args.cwd, base:args.cwd}),
coffee(args.coffeeOpts),
gulp.dest(args.cwd)
);
});
}
function defineBuildTask(args) {
gulp.task('build', ['less', 'coffee', 'install'], function(cb) {
build(args, cb);
});
}
function build(args, cb) {
try {
var pkg = new umi.Package(args.cwd, {
skip: args.skip,
ignore: args.ignore,
moduleDir: spmrc.get('install.path')
});
if (!args.noPkgLog) {
log.info('package', 'analyse infomation');
log.info('package', 'dependencies: ' + Object.keys(pkg.dependencies));
log.info('package', 'files: ' + Object.keys(pkg.files));
}
args.pkg = pkg;
} catch(err) {
cb(err);
}
var files = getFiles(pkg);
log.info('output', 'files: ' + files);
// check duplicatate
checkDuplicate(files, pkg);
// build package in dependencies
var depFiles = {};
if (args.withDeps) {
var depPkgs = getAllDependencies(pkg);
for (var id in depPkgs) {
files = files.concat(getFiles(depPkgs[id]).map(function(f) {
var filepath = spmrc.get('install.path') + '/' + id.replace('@','/') + '/' + f;
depFiles[filepath] = depPkgs[id].files[f];
return filepath;
}));
}
log.info('withDeps', 'files: ' + files);
}
// check ext deps and install
if (!args.noExtCheck) {
var extDeps = getExtDeps(files, pkg, depFiles);
if (extDeps.length) {
log.info('extDeps', extDeps.join(','));
}
if (extDeps.length) {
return install({
cwd: args.cwd,
save: true,
name: extDeps
}, function(err) {
if (err) {
return cb(err);
}
args.noExtCheck = args.noPkgLog = true;
build(args, cb);
});
}
}
var isStandalone;
if (args.include === 'standalone') {
isStandalone = true;
args.include = 'all';
}
if (args.include === 'umd') {
isStandalone = true;
args.include = 'all';
// use package name as global name for umd
args.umd = camelCase(args.pkg.name);
}
// add {{name}} and {{version}} support for pathmap
if (args.pathmap) {
args.pathmap = template(args.pathmap, pkg);
}
var opt = umi.util.extendOption(args);
var optDebug = umi.util.extendOption(args);
optDebug.rename = {'suffix': '-debug'};
return pipe(
gulp.src(files, {cwd:opt.cwd, base:opt.cwd}),
args.spmfile.startStream ? args.spmfile.startStream(args) : through.obj(),
// transport css and js
gulpif(isCSSorJS, mirror(
// normal
pipe(
transport(opt),
gulpif(isJS, pipe(
gulpif(isStandalone, standalonify(args)),
uglify(args.uglifyOpts)
), pipe(
gulpif(args.autoprefixer, autoprefixer(args.autoprefixer)),
cssmin(args.cssminOpts)
))
),
// debug
pipe(
transport(optDebug),
gulpif(isJS, pipe(
gulpif(isStandalone, standalonify(args))
), pipe(
gulpif(args.autoprefixer, autoprefixer(args.autoprefixer))
))
)
)),
transport.plugin.dest(opt),
slash(),
args.pathmap ? pathmap(args.pathmap) : through.obj(),
args.spmfile.endStream ? args.spmfile.endStream(args) : through.obj(),
gulp.dest(args.dest)
).on('end', cb);
}
//////////////////
// Utils
function getFiles(pkg) {
var files = [];
if (fs.existsSync(path.join(pkg.dest, pkg.main))) {
files.push(pkg.main);
}
pkg.output.forEach(function(_glob) {
var items = glob.sync(_glob, {cwd: pkg.dest});
files = files.concat(items);
});
return uniq(files);
}
function getLessFiles(args) {
var pkg = require(join(args.cwd, 'package.json'));
var files = [];
if (Array.isArray(pkg.spm.output)) {
pkg.spm.output.forEach(function(_glob) {
_glob = _glob.replace(/\.css$/, '.less');
var items = glob.sync(_glob, {cwd: args.cwd});
files = files.concat(items);
});
}
return uniq(files).filter(function(file) {
return path.extname(file) === '.less';
});
}
function getCoffeeFiles(args) {
var pkg = require(join(args.cwd, 'package.json'));
var files = [];
if (Array.isArray(pkg.spm.output)) {
pkg.spm.output.forEach(function(_glob) {
_glob = _glob.replace(/\.js/, '.coffee');
var items = glob.sync(_glob, {cwd: args.cwd});
files = files.concat(items);
});
}
return uniq(files).filter(function(file) {
return path.extname(file) === '.coffee';
});
}
function pipe() {
return multipipe.apply(null, arguments)
.on('data', function() {});
}
function showLog(type, e) {
var info = 'task ' + e.task;
if (type === 'end') info = info.to.green.color;
log.info(type, info);
}
function logArgs(args) {
Object.keys(args)
.forEach(function(key) {
var val = args[key];
if (typeof val === 'object') {
val = JSON.stringify(val);
}
log.info('arguments', key + ' = ' + val);
});
}
function isJS(file) {
return path.extname(file.path) === '.js';
}
function isCSSorJS(file) {
return path.extname(file.path) === '.js' ||
path.extname(file.path) === '.css';
}
function camelCase(str) {
return str.replace(/[_.-](\w|$)/g, function (_,x) {
return x.toUpperCase();
});
}
function template(format, data) {
return format.replace(/{{([a-z]*)}}/g, function(all, match) {
return data[match] || '';
});
}
function noop() {}
function getAllDependencies(pkg) {
var result = {};
recurseDependencies(pkg);
return result;
function recurseDependencies(pkg) {
Object.keys(pkg.dependencies).forEach(function(key) {
var id = pkg.dependencies[key].id;
if (!result[id]) {
result[id] = pkg.dependencies[key];
recurseDependencies(pkg.dependencies[key]);
}
});
}
}
function getExtDeps(files, pkg, depFiles) {
var extDeps = {};
files.forEach(function(f) {
if (path.extname(f) !== '.js') return;
var file = depFiles[f] || pkg.files[f];
if (!file) return;
if (!pkg.dependencies['import-style'] && file.hasExt('css')) {
extDeps['import-style'] = true;
}
if (!pkg.dependencies['handlebars-runtime'] && file.hasExt('handlebars')) {
extDeps['handlebars-runtime'] = true;
}
});
return Object.keys(extDeps);
}
function checkDuplicate(files, pkg) {
var rootPkgName = pkg.name;
files.forEach(function(f) {
var dup = {};
var file = pkg.files[f];
file.lookup(function(fileInfo) {
var name = fileInfo.pkg.name;
var version = fileInfo.pkg.version;
if (name === rootPkgName) return;
dup[name] = dup[name] || {};
dup[name][version] = true;
});
for (var k in dup) {
var versions = Object.keys(dup[k]);
if (versions.length > 1) {
log.warn('dulplicate',
'%s (%s) while building %s'.to.yellow.color,
k,
versions.join(', '),
file.path
);
}
}
});
}
{
"name": "spm-build-example",
"version": "0.1.0",
"description": "SPM build for project.",
"main": "index",
"author": "chencheng <yunqian@alibaba-inc.com>",
"license": "MIT",
"scripts": {
"test": "make test"
},
"dependencies": {
"array-uniq": "~1.0.0",
"checkonline": "~0.3.0",
"co": "~3.1.0",
"colorful": "~2.1.0",
"extend": "~1.3.0",
"glob": "~4.0.5",
"gulp": "~3.6.2",
"gulp-autoprefixer": "~1.0.0",
"gulp-clean": "~0.3.1",
"gulp-coffee": "~2.1.2",
"gulp-cssmin": "~0.1.6",
"gulp-if": "~1.2.4",
"gulp-less": "~1.3.5",
"gulp-mirror": "~0.3.0",
"gulp-pathmap": "~0.1.0",
"gulp-slash": "~1.1.3",
"gulp-template": "~1.0.0",
"gulp-transport": "~0.7.0",
"gulp-uglify": "~0.3.2",
"multipipe": "~0.1.1",
"spm-client": "~0.2.4",
"spm-log": "~0.1.1",
"spm-standalonify": "~0.1.0",
"spmrc": "~1.2.0",
"through2": "~0.6.1",
"umi": "~0.6.0"
},
"devDependencies": {
"coveralls": "2",
"istanbul": "0",
"mocha": "1",
"should": "3",
"sinon": "~1.10.3"
},
"publishConfig": {
"registry": "http://registry.npm.alibaba-inc.com"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment