Authored by 514335620@qq.com

合并develop 分支代码

... ... @@ -12,13 +12,25 @@ if (config.useOneapm) {
require('oneapm');
}
let express = require('express'),
path = require('path'),
bodyParser = require('body-parser'),
cookieParser = require('cookie-parser'),
favicon = require('serve-favicon');
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const favicon = require('serve-favicon');
const session = require('express-session');
const memcached = require('connect-memcached');
const pkg = require('./package.json');
let app = express();
const app = express();
const MemcachedStore = memcached(session);
// 向模板注入变量
app.locals.devEnv = app.get('env') === 'development';
app.locals.version = pkg.version;
// 指定libray目录
global.library = path.resolve('./library');
app.set('view engine', '.hbs');
... ... @@ -27,11 +39,22 @@ app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(session({
secret: '3e5fec7deca0b8305cefe2ad9d90ff5e',
name: 'PHPSESSID',
prefix: 'yohobuy',
proxy: true,
resave: true,
saveUninitialized: true,
store: new MemcachedStore({
hosts: config.memcache.session
})
}));
// dispatcher
require('./dispatch')(app);
// listener
app.listen(6002, function() {
app.listen(config.port, function() {
console.log('yohobuy start');
});
... ...
/**
* index controller
* @author: bikai<kai.bi@yoho.cn>
* @date: 2016/05/16
*/
'use strict';
exports.special = (req, res) => {
let id = req.params[0] || 0;
res.send(id);
};
... ...
/**
* sub app index
* @author: bikai<kai.bi@yoho.cn>
* @date: 2016/05/16
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: 'helpers'
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* router of sub app index
* @author: bikai<kai.bi@yoho.cn>
* @date: 2016/05/16
*/
'use strict';
const express = require('express');
const router = express.Router(); // eslint-disable-line
const cRoot = './controllers';
const specialController = require(`${cRoot}/special`);
// 专题活动
router.get(/^\/special\/(\d+)_(.*)\.html$/, specialController.special);
module.exports = router;
... ...
<div class="yoho-page center-content">
<p>body</p>
</div>
... ...
/**
* girls controller
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/05/16
*/
'use strict';
const headerModel = require('../../../doraemon/models/header');
// const channelModel = require('../models/index');
exports.boysIndex = (req, res) => {
headerModel.requestHeaderData()
.then(response => {
let data = headerModel.setHeaderData(response.data, 'boys');
data.module = 'index';
data.page = 'index';
data.footerTop = true;
res.render('boys', data);
})
.catch(() => {
res.render('error', {devEnv: true, pageErr: true});
});
};
exports.girlsIndex = (req, res) => {
headerModel.requestHeaderData()
.then(response => {
let data = headerModel.setHeaderData(response.data, 'girls');
data.module = 'index';
data.page = 'index';
data.footerTop = true;
res.render('girls', data);
})
.catch(() => {
res.render('error', {devEnv: true, pageErr: true});
});
};
... ...
/**
* sub app girls
* @author: biao<bill.zhao@yoho.cn>
* @date: 2016/05/16
*/
var express = require('express'),
path = require('path'),
hbs = require('express-handlebars');
var app = express();
// set view engin
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: ['./views/partial', `${doraemon}/partial`],
helpers: 'helpers'
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
* girls model
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/05/17
*/
'use strict';
// const _ = require('lodash');
const ServiceAPI = require(`${global.library}/api`).ServiceAPI;
const sign = require(`${global.library}/sign`);
const serviceApi = new ServiceAPI();
/**
* 获取公共配置
* @param undefined
* @return {Object} 配置对象
*/
// const commonConfig = () => ({
// title: 'girls'
// });
exports.getContent = () => {
let data = sign.apiSign({
/* eslint-disable */
client_type: 'web'
/* eslint-enable */
});
serviceApi.get('/operations/api/v5/resource/home', data).then(response => {
console.log(response);
});
};
... ...
/**
* router of sub app girls
* @author: biao<bill.zhao@yoho.cn>
* @date: 2016/05/16
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
// Your controller here
const channelController = require(`${cRoot}/index`);
router.get('/boys', channelController.boysIndex);
module.exports = router;
... ...
... ... @@ -9,19 +9,17 @@
const headerModel = require('../../../doraemon/models/header');
exports.index = (req, res) => {
headerModel.getHeaderData()
.then(function(modelRes) {
let response = JSON.parse(modelRes);
let data = headerModel.getAllHeaderData(response.data, 'boy');
headerModel.requestHeaderData()
.then(response => {
let data = headerModel.setHeaderData(response.data, 'boys');
data.headerData.navbars[0].active = true;
data.module = 'index';
data.page = 'index';
data.footerTop = true;
res.render('index', data);
})
.catch(function() {
.catch(() => {
res.render('index', {devEnv: true, pageErr: true});
});
};
... ...
... ... @@ -10,12 +10,20 @@ const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
module.exports = {
port: 6002,
domains: {
api: 'http://192.168.102.205:8080/gateway',
service: 'http://testservice.yoho.cn:28077',
search: 'http://192.168.10.64:8080/yohosearch/'
},
useOneapm: false,
useCache: true,
memcache: {
master: ['192.168.102.168:12580'],
slave: ['192.168.102.168:12580'],
session: ['192.168.102.168:12580'],
timeout: 5000
},
loggers: {
infoFile: {
name: 'info',
... ...
... ... @@ -8,7 +8,13 @@ module.exports = app => {
// 公共服务
// 专题活动等活动页面
app.use(require('./apps/activity'));
// 业务模块
app.use('/', require('./apps/index'));
app.use('/product', require('./apps/product'));
// 频道页
app.use('/', require('./apps/channel'));
};
... ...
... ... @@ -7,10 +7,9 @@
'use strict';
const _ = require('lodash');
const lRoot = '../../library/';
const ServiceAPI = require(`${lRoot}/api`).ServiceAPI;
const sign = require(`${lRoot}/sign`);
const ServiceAPI = require(`${global.library}/api`).ServiceAPI;
const sign = require(`${global.library}/sign`);
const serviceApi = new ServiceAPI();
... ... @@ -20,11 +19,22 @@ const serviceApi = new ServiceAPI();
* @return {Object} 配置对象
*/
const commonConfig = () => ({
title: 'home',
devEnv: true,
version: '0.0.1'
title: 'home'
});
const getChannelIndex = (type) => {
const channelMap = {
boys: 0,
girls: 1,
kids: 2,
lifestyle: 3
};
const index = channelMap[type];
return _.isNil(index) ? 0 : index;
};
/**
* 获取菜单
* @param undefined
... ... @@ -138,26 +148,7 @@ const getThirdNav = (data) => {
const getSubNav = (data, type) => {
let subNav = [];
let index;
switch (type) {
case 'boy':
index = 0;
break;
case 'girl':
index = 1;
break;
case 'kids':
index = 2;
break;
case 'lifestyle':
index = 3;
break;
default:
index = 0;
}
_.forEach(data[index].sub, function(item) {
_.forEach(data[getChannelIndex(type)].sub, function(item) {
let obj = {};
obj.link = item.sort_url;
... ... @@ -177,23 +168,28 @@ const getSubNav = (data, type) => {
};
/**
* 处理接口返回的数据
* @param {object} 接口返回的对象
* @param {String} 指定页面类型为boys,girls,kids,lifestyle
* @return {object} 头部数据
*/
exports.getAllHeaderData = function(resData, type) {
exports.setHeaderData = (resData, type) => {
let config = commonConfig();
let data = {
headerData: {
header: true,
type: type,
headType: type,
yohoGroup: getMenuData(),
navbars: getNavBar(resData),
subNav: getSubNav(resData, type)
}
};
data.headerData.navbars[getChannelIndex(type)].active = true;
return _.merge(config, data);
};
... ... @@ -202,15 +198,13 @@ exports.getAllHeaderData = function(resData, type) {
* @param undefined
* @return {promise}
*/
exports.getHeaderData = function() {
exports.requestHeaderData = () => {
let data = sign.apiSign({
/* eslint-disable */
client_type: 'web',
client_type: 'web'
/* eslint-enable */
});
return serviceApi.get('/operations/api/v6/category/getCategory', data);
return serviceApi.get('/operations/api/v6/category/getCategory', data, true);
};
... ...
... ... @@ -15,7 +15,7 @@
<link rel="dns-prefetch" href="//img12.static.yhbimg.com">
<link rel="dns-prefetch" href="//img13.static.yhbimg.com">
{{#if devEnv}}
<link rel="stylesheet" href="//localhost:5000/css/index.css">
<link rel="stylesheet" href="//localhost:5002/css/index.css">
{{^}}
<link rel="stylesheet" href="//cdn.yoho.cn/m-yohobuy-node/{{version}}/index.css">
{{/if}}
... ... @@ -29,7 +29,7 @@
{{/if}}
{{> footer}}
{{#if devEnv}}
<script src="//localhost:5000/{{module}}.{{page}}.js"></script>
<script src="//localhost:5002/{{module}}.{{page}}.js"></script>
{{^}}
<script src="//cdn.yoho.cn/m-yohobuy-node/{{version}}/{{module}}.{{page}}.js"></script>
{{/if}}
... ...
{{# headerData}}
<div class="yoho-header {{headtype}}">
<div class="yoho-header {{headType}}">
<div class="tool-wrapper clearfix">
<div class="center-content">
<div class="yoho-group-map left">
... ...
... ... @@ -7,12 +7,16 @@
'use strict';
const rp = require('request-promise');
const qs = require('querystring');
const md5 = require('md5');
const _ = require('lodash');
const log = require('./logger');
const api = require('../config/common').domains.api;
const serviceApi = require('../config/common').domains.service;
const searchApi = require('../config/common').domains.search;
const cache = require('./cache');
const Timer = require('./timer');
const config = require('../config/common');
const api = config.domains.api;
const serviceApi = config.domains.service;
const searchApi = config.domains.search;
let ApiUrl;
... ... @@ -24,54 +28,107 @@ class API {
}
/**
* get
* @param url String
* @param data Obejct
* 获取请求 ID
*/
get(url, data) {
let options = {
url: `${ApiUrl}${url}`,
qs: data
};
_getReqId(options) {
return md5(`${options.url}?${qs.stringify(options.qs || options.form)}`);
}
/**
* 调用接口
*/
_requestFromAPI(options, cacheOption, reqId) {
let timer = new Timer();
timer.put('getApi');// 统计时间开始
let ret = rp(options);
ret.then((body)=>{
let duration = timer.put('getApi');// 接口返回
log.info('API GET: %s, parms: %j , durationMs: %d ms , body: %s', options.url, options.qs, duration, body);
log.info(`get api: ${options.url}?${qs.stringify(options.qs)}`);
return rp(options).then((result) => {
let duration = timer.put('getApi');// 统计时间结束
log.info(`get api success: use: ${duration}ms`);
if (config.useCache && cacheOption) {
reqId = reqId || this._getReqId(options);
// 数据校验无误,写缓存, 否则返回 Slave 缓存服务器的数据
if (result && result.code) {
let cacheTime = _.isNumber(cacheOption) ? cacheOption : 60;
cache.set(`apiCache:${reqId}`, result, cacheTime);
cache.setSlave(`apiCache:${reqId}`, result, cacheTime);
} else {
return this._requestFromCache(options, true);
}
}
return result;
}).catch((error)=>{
let duration = timer.put('getApi');// 接口返回
let duration = timer.put('getApi');// 统计时间结束
log.error('API GET: %s, parms: %j , durationMs: %d ms error: %s , statusCode: %d',
options.url, options.qs, duration, error.message, error.statusCode);
});
log.error(`get api fail: use: ${duration}ms, statusCode: ${error.statusCode}, error: ${error.message}`);
return ret;
// 使用缓存的时候,读取二级缓存
if (config.useCache) {
return this._requestFromCache(options, true);
}
return Promise.reject({
error: '接口调用失败'
});
});
}
/**
* multi get
* @params: urls => Array[Object[url[string], data[object]]]
* 读取缓存
* @param {[object]} options
* @param {[boolean]} slave true: 读取二级缓存
* @return {[type]}
*/
multiGet(urls) {
var rps = [];
_.forEach(urls, function(el) {
rps.push(rp({
url: `${ApiUrl}${el.url}`,
qs: el.data
}));
_requestFromCache(options, slave) {
let reqId = this._getReqId(options);
let getCache = slave ? cache.getFromSlave : cache.get;
log.info(`get cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`);
return getCache(`apiCache:${reqId}`).then((result) => {
if (!_.isNil(result)) {
try {
result = JSON.parse(result);
} finally {
log.info(slave ? 'get slave cache success' : 'get master cache success');
return result;
}
}
if (!slave) {
return this._requestFromAPI(options, true, reqId);
}
}).catch(() => {
log.error(slave ? 'get slave cache fail' : 'get master cache fail');
if (!slave) {
return this._requestFromAPI(options, true, reqId);
}
});
}
return Promise.all(rps).then((d) => {
return d;
});
/**
* 使用 get 请求获取接口
* @param {[string]} url
* @param {[object]} data
* @param {[bool or number]} cacheOption 使用数字时,数字表示缓存时间
* @return {[type]}
*/
get(url, data, cacheOption) {
let options = {
url: `${ApiUrl}${url}`,
qs: data,
json: true,
timeout: 3000
};
// 从缓存获取数据
if (config.useCache && cacheOption) {
return this._requestFromCache(options);
}
return this._requestFromAPI(options, cacheOption);
}
/**
... ... @@ -80,11 +137,22 @@ class API {
* @param data Obejct
*/
post(url, data) {
return rp({
let options = {
url: `${ApiUrl}${url}`,
form: data,
method: 'post',
form: data
});
json: true,
timeout: 3000
};
return this._requestFromAPI(options);
}
all(list) {
if (_.isArray(list)) {
return Promise.all(list);
}
throw Error('the parameters of api all method should be Array!');
}
}
... ...
/**
* 缓存封装
* 前期使用 memcache, 写方法的时候考虑一下如何转换为 redis
* @author bikai kai.bi@yoho.cn
* @date 2016/05/16
*/
'use strict';
const Promise = require('bluebird');
const Memcached = require('memcached');
const _ = require('lodash');
const log = require('./logger');
const config = require('../config/common');
let master = new Memcached(config.memcache.master, config.memcache);
let slave = new Memcached(config.memcache.slave, config.memcache);
master = Promise.promisifyAll(master);
slave = Promise.promisifyAll(slave);
/**
* 获取缓存
* @param {[string]} key 键
* @return {[type]}
*/
exports.get = (key) => {
if (_.isString(key)) {
return master.getAsync(key);
}
return Promise.resolve();
};
/**
* 批量获取缓存
* @param {[array]} list 字符串数组
* @return {[type]}
*/
exports.getMulti = (list) => {
if (_.isArray(list)) {
return master.getMultiAsync(list);
}
return Promise.resolve();
};
/**
* 获取缓存(从 Slave 服务器)
* @param {[string]} key 键
* @return {[type]}
*/
exports.getFromSlave = (key) => {
if (_.isString(key)) {
return slave.getAsync(key);
}
return Promise.resolve();
};
/**
* 批量获取缓存(从 Slave 服务器)
* @param {[array]} list 字符串数组
* @return {[type]}
*/
exports.getMultiFromSlave = (list) => {
if (_.isArray(list)) {
return slave.getMultiAsync(list);
}
return Promise.resolve();
};
/**
* 写缓存
* @param {[type]} key 键
* @param {[type]} value 值
* @param {[type]} lifetime 生命周期
* @return {[type]}
*/
exports.set = (key, value, lifetime) => {
lifetime = lifetime || 86400;
log.info(`write cache: ${key}`);
if (_.isObject(value)) {
value = JSON.stringify(value);
}
if (_.isString(key)) {
return master.setAsync(key, value, lifetime);
}
return Promise.resolve();
};
/**
* 写缓存(到 Slave 服务器)
* @param {[type]} key 键
* @param {[type]} value 值
* @param {[type]} lifetime 生命周期
* @return {[type]}
*/
exports.setSlave = (key, value, lifetime) => {
lifetime = lifetime || 86400;
if (_.isObject(value)) {
value = JSON.stringify(value);
}
if (_.isString(key)) {
return slave.setAsync(key, value, lifetime);
}
return Promise.resolve();
};
/**
* 删除缓存
* @param {[string]} key 键
* @return {[type]}
*/
exports.del = (key) => {
if (_.isString(key)) {
return master.delAsync(key);
}
return Promise.resolve();
};
... ...
/**
* 对象键名驼峰化
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
'use strict';
const _ = require('lodash');
let camelCase,
camelCaseObject,
camelCaseArray;
camelCaseObject = (obj) => {
_.forEach(Object.keys(obj), (k) => {
obj[k] = camelCase(obj[k]);
obj[_.camelCase(k)] = obj[k];
});
return obj;
};
camelCaseArray = (list) => {
_.forEach(list, (k) => {
k = camelCase(k);
});
return list;
};
camelCase = (data) => {
if (_.isObject(data)) {
data = camelCaseObject(data);
}
if (_.isArray(data)) {
data = camelCaseArray(data);
}
return data;
};
module.exports = camelCase;
... ...
/*
* @Author: Targaryen
* @Date: 2016-05-10 10:11:34
* @Last Modified by: Targaryen
* @Last Modified time: 2016-05-12 11:25:53
/**
* Handlebars helpers
* bikai kai.bi@yoho.cn
* 2016-05-10
*/
/* -----------------------------------------
* 实现 PHP 的 Helpers 函数
* -----------------------------------------
*/
'use strict';
const SUB_DOMAIN = '.yohobuy.com';
const SITE_MAIN = 'http://www.yohobuy.com';
const querystring = require('querystring');
const _ = require('lodash');
const config = require('../config/common');
class HELPERS {
/**
* 构建网站的URL
* @param uri String
* @param param Array
* @param module String
*/
url(uri, param, module) {
let url = '';
if (module === null) {
module = 'index';
}
switch (module) {
case 'default':
url = 'http://m.yohobuy.com';
break;
case 'guang':
url = 'http://guang' + SUB_DOMAIN;
break;
case 'list':
url = 'http://list' + SUB_DOMAIN;
break;
case 'search':
url = 'http://search' + SUB_DOMAIN;
break;
case 'index':
url = SITE_MAIN;
break;
case '':
break;
case undefined:
break;
default:
url = 'http://' + module + SUB_DOMAIN;
break;
}
/**
* 七牛图片路径处理
* @param {[tring]} url
* @param {[tring]} width
* @param {[tring]} height
* @param {[tring]} mode
* @return {[tring]}
*/
exports.image = (url, width, height, mode) => {
mode = _.isNumber(mode) ? mode : 2;
url = url || '';
return url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);
};
url += uri;
if (param !== null && param !== undefined) {
url += '?';
_.forEach(param, function(value, key) {
url += (key + '=' + value + '&');
});
}
if (url.substr(-1, 1) === '&') {
return url.substr(0, url.length - 1);
} else {
return url;
}
/**
* 站内地址格式化
* @param {[string]} uri 路径
* @param {[object]} qs 查询字符串
* @param {[string]} module 模块
* @return {[string]}
*/
exports.url = (uri, qs, module) => {
const subDomain = '.m.yohobuy.com';
const subName = {
default: config.siteUrl,
guang: '//guang' + subDomain,
list: '//list' + subDomain,
search: '//search' + subDomain,
huodong: '//huodong' + subDomain,
activity: '//activity.yohobuy.com',
index: config.siteUrl
};
let url;
module = module || 'default';
if (subName[module]) {
url = subName[module];
} else {
url = '//' + module + subDomain; // 规则没匹配到就把模块当作子域名
}
/**
* 根据尺寸获得图片url
* @param uri String
* @param param Array
* @param module String
*/
getImageUrl(url, width, height, mode) {
if (mode === null) {
mode = 2;
}
return url.replace(/{width}/g, width)
.replace(/{height}/g, height)
.replace(/{mode}/g, mode);
url += uri;
if (qs) {
url += '?' + querystring.stringify(qs);
}
}
module.exports = HELPERS;
return url;
};
/**
* 大写转小写处理
* @param {[string]} str 转换字符
*/
exports.lowerCase = (str) => {
str = str || '';
return str.toLowerCase();
};
/**
* 小写转大写处理
* @param {[string]} str 转换字符
*/
exports.upperCase = (str) => {
str = str || '';
return str.toUpperCase();
};
... ...
... ... @@ -5,7 +5,6 @@
*/
'use strict';
const _ = require('lodash');
const md5 = require('md5');
... ... @@ -24,7 +23,7 @@ const privateKey = {
* @return {Object} 排序之后的参数对象
*/
const packageSort = argument => {
var newObj = {};
let newObj = {};
for (let k of Object.keys(argument).sort()) {
newObj[k] = argument[k];
... ... @@ -39,7 +38,7 @@ const packageSort = argument => {
* @return {string} 生成的签名字符串
*/
const makeSign = argument => {
var qs = [];
let qs = [];
_.forEach(argument, function(value, key) {
qs.push(key + '=' + _.trim(value));
... ... @@ -50,11 +49,10 @@ const makeSign = argument => {
// 生成API签名,调用后端接口的时候有私钥校验
exports.apiSign = (params) => {
var clientType = params.client_type || 'h5';
const clientType = params.client_type || 'web';
/* eslint-disable */
var sign = packageSort(Object.assign({
let sign = packageSort(Object.assign({
client_type: clientType,
private_key: privateKey[clientType],
app_version: '3.8.2',
... ... @@ -62,18 +60,18 @@ exports.apiSign = (params) => {
screen_size: '720x1280',
v: '7'
}, params));
/* eslint-enable */
return Object.assign(sign, {
sign = Object.assign(sign, {
client_secret: makeSign(sign) // eslint-disable-line camelcase
});
delete sign.private_key;
return sign;
};
// 检查签名,APP 访问 H5 页面的时候需要检查
exports.checkSign = (params) => {
var clientSecret = params.client_secret, // eslint-disable-line camelcase
let clientSecret = params.client_secret, // eslint-disable-line camelcase
sortedParams;
// 忽略部分参数
... ... @@ -90,7 +88,7 @@ exports.checkSign = (params) => {
// 检查签名,APP 访问 H5 页面的时候需要检查, 有可能不同于上边的签名方式
exports.webSign = (params) => {
var webPrivateKey = 'yohobuyapp';
const webPrivateKey = 'yohobuyapp';
return params.key === md5(md5(webPrivateKey) + params.uid);
};
... ...
... ... @@ -18,13 +18,17 @@
},
"license": "MIT",
"dependencies": {
"bluebird": "^3.3.5",
"body-parser": "^1.15.0",
"connect-memcached": "^0.2.0",
"cookie-parser": "^1.4.1",
"express": "^4.13.1",
"express-handlebars": "^3.0.0",
"express-session": "^1.13.0",
"influxdb-winston": "^1.0.1",
"lodash": "^4.12.0",
"md5": "^2.1.0",
"memcached": "^2.2.1",
"morgan": "^1.7.0",
"oneapm": "^1.2.20",
"request-promise": "^3.0.0",
... ... @@ -34,7 +38,7 @@
},
"devDependencies": {
"autoprefixer": "^6.3.6",
"eslint": "^2.9.0",
"eslint": "^2.10.2",
"eslint-config-yoho": "^1.0.1",
"gulp": "^3.9.1",
"gulp-cssnano": "^2.1.2",
... ... @@ -55,14 +59,17 @@
"postcss-position": "^0.4.0",
"postcss-short": "^1.4.0",
"postcss-sprites": "^3.1.2",
"postcss-use": "^2.0.2",
"postcss-use": "^2.1.0",
"precss": "^1.4.0",
"rewire": "^2.5.1",
"shelljs": "^0.7.0",
"stylelint": "^6.3.3",
"stylelint-config-yoho": "^1.2.2",
"stylelint-config-yoho": "^1.2.3",
"webpack": "^1.13.0",
"webpack-dev-server": "^1.14.1",
"webpack-stream": "^3.1.0"
"webpack-stream": "^3.1.0",
"yoho-handlebars": "^4.0.5",
"yoho-jquery": "^1.9.1",
"yoho-jquery-lazyload": "^1.9.7"
}
}
... ...
... ... @@ -167,16 +167,19 @@ gulp.task('webpack-dev-server', () => {
new WebpackDevServer(webpack(devConfig), {
contentBase: '.',
publicPath: '//localhost:5000',
publicPath: '//localhost:5002/',
hot: true,
stats: {
colors: true
},
headers: {
'Access-Control-Allow-Origin': '*'
}
}).listen(5000, 'localhost', (err) => {
}).listen(5002, 'localhost', (err) => {
if (err) {
throw new gutil.PluginError('webpack-dev-server', err);
}
gutil.log('[webpack-serve]', 'http://localhost:5000/');
gutil.log('[webpack-serve]', 'http://localhost:5002/');
});
});
... ...