gulpfile.js 7.86 KB
/**
 * Yohobuy 构建脚本
 * @author: xuqi<qi.xu@yoho.cn>
 * @date: 2016/4/25
 */

'use strict';

const path = require('path');
const gulp = require('gulp');
const gutil = require('gulp-util');
const ftp = require('gulp-ftp');
const os = require('os');

const postcss = require('gulp-postcss');
const sourcemaps = require('gulp-sourcemaps');
const cssnano = require('gulp-cssnano');
const scss = require('postcss-scss');

const webpack = require('webpack');
const WebpackDevServer = require('webpack-dev-server');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const UglifyJsParallelPlugin = require('webpack-uglify-parallel');

const webpackConfig = require('./webpack.config.js');

const env = {
    dev: Symbol('development'),
    pro: Symbol('production')
};

const config = require('../package.json');
const ftpConfig = {
    host: '218.94.75.58',
    user: 'php',
    pass: 'yoho9646'
};
const distRoot = `dist/${config.name}`;
const dist = {
    js: `${distRoot}/${config.version}`,
    css: `${distRoot}/${config.version}`,
    assets: `${distRoot}/assets`,
    img: `${distRoot}/assets/img`,
    font: `${distRoot}/assets/font`
};

/**
 * postcss plugins for both dev and pro
 * @parem et Symbol
 */
const postcssPlugin = (et) => {
    let sprites = {
            spritesmith: {
                padding: 8
            },
            filterBy(file) {
                // base64 的图片没有 url 过滤掉
                if (file.url) {
                    return Promise.resolve();
                }
                return Promise.reject();
            },
            groupBy(file) {
                let group = file.url.split('/')[1];
                let entry = path.basename(file.styleFilePath);

                file.retina = true;
                file.radio = 2;

                if (group) {
                    group = entry + '.' + group;
                }

                return group ? Promise.resolve(group) : Promise.reject('yoho');
            }
        },
        assets,
        plugins;

    // assets & sprites config in both dev and pro
    if (et === env.pro) {
        assets = {
            loadPaths: [dist.img, dist.font],
            relativeTo: dist.css
        };

        Object.assign(sprites, {
            basePath: dist.img,
            stylesheetPath: dist.css,
            spritePath: dist.img
        });
    } else if (et === env.dev) {
        assets = {
            loadPaths: ['img/', 'font/'],
            relativeTo: 'css/'
        };

        Object.assign(sprites, {
            basePath: 'img/',
            stylesheetPath: 'bundle/',
            spritePath: 'bundle/'
        });
    }

    plugins = [
        require('postcss-import')({
            resolve (id) {
                let name = path.basename(id);

                if (!/^_/.test(name)) {
                    id = path.dirname(id) + '/_' + name;
                }
                return id;
            }
        }),
        require('precss'),
        require('postcss-sprites').default(sprites),
        require('postcss-assets')(assets),
        require('postcss-calc'),
        require('postcss-pxtorem')({
            rootValue: 40,
            unitPrecision: 5, // 保留5位小数字
            minPixelValue: 2, // 小于 2 时,不转换
            selectorBlackList: [], // 选择器黑名单,可以使用正则
            propWhiteList: [] // 属性名称为空,表示替换所有属性的值
        }),
        require('autoprefixer')({
            browsers: ['> 1%']
        }),

        // 可选
        require('postcss-use')({
            modules: ['postcss-clearfix', 'postcss-crip', 'postcss-short', 'postcss-center', 'postcss-position']
        })
    ];

    if (et === env.pro) {
        plugins.push(require('postcss-cachebuster')({
            imagesPath: `/${dist.img}`,
            cssPath: `/${dist.css}`
        }));
    }
    return plugins;
};

// default
gulp.task('default', ['postcss-dev', 'postcss-watch', 'webpack-dev-server']);

// ge
gulp.task('ge', ['postcss', 'webpack']);

// dist
gulp.task('dist', ['ge'], () => {
    const ftpstream = ftp(ftpConfig);

    return gulp.src('dist/**/')
        .pipe(ftpstream)
        .pipe(gutil.noop());
});

// postcss compile in dev
gulp.task('postcss-dev', () => {
    return gulp.src(['scss/index.css', 'scss/common.css'])
        .pipe(sourcemaps.init())
        .pipe(postcss(postcssPlugin(env.dev), {
            parser: require('postcss-scss')
        }))
        .pipe(sourcemaps.write('.'))
        .pipe(gulp.dest('bundle/'));
});

// postcss file watch
gulp.task('postcss-watch', () => {
    gulp.watch('scss/**/*.css', ['postcss-dev']);
});

// copy assets
gulp.task('assets', ['img', 'font']);

// copy img
gulp.task('img', () => {
    return gulp.src('img/**/*')
        .pipe(gulp.dest(dist.img));
});

// copy font
gulp.task('font', () => {
    return gulp.src('font/**/*')
        .pipe(gulp.dest(dist.font));
});

// postcss compile in pro
gulp.task('postcss', ['assets'], () => {
    return gulp.src(['scss/index.css', 'scss/common.css'])
        .pipe(postcss(postcssPlugin(env.pro), {
            parser: require('postcss-scss')
        }))
        .pipe(cssnano({
            safe: true
        }))
        .pipe(gulp.dest(dist.css));
});

// webpack dev server
gulp.task('webpack-dev-server', () => {
    const devConfig = Object.assign({}, webpackConfig, {
        devtool: '#inline-source-map',
        vue: {
            postcss: {
                plugins: postcssPlugin(env.dev),
                options: {
                    parser: scss
                }
            },
            autoprefixer: false,
            loaders: {
                css: ExtractTextPlugin.extract(['css?-url&sourceMap'])
            }
        },
        postcss: {
            plugins: postcssPlugin(env.dev),
            options: {
                parser: scss
            }
        }
    });

    devConfig.output.publicPath = 'http://localhost:5001/';

    // 开发环境插件
    devConfig.plugins[0] = new ExtractTextPlugin('bundle/[name].css');
    devConfig.plugins.push(new webpack.HotModuleReplacementPlugin());

    devConfig.entry.libs.unshift(`webpack-dev-server/client?${devConfig.output.publicPath}`, 'webpack/hot/dev-server');

    new WebpackDevServer(webpack(devConfig), {
        contentBase: '.',
        publicPath: devConfig.output.publicPath,
        hot: true,
        inline: true,
        quiet: true,
        stats: {
            colors: true
        },
        headers: {
            'Access-Control-Allow-Origin': '*'
        }
    }).listen(5001, '0.0.0.0', (err) => {
        if (err) {
            throw new gutil.PluginError('webpack-dev-server', err);
        }
        gutil.log('[webpack-serve]', devConfig.output.publicPath);
    });
});

// webpack compile in pro
gulp.task('webpack', ['assets'], (done) => {
    const proConfig = Object.assign({}, webpackConfig, {
        vue: {
            postcss: {
                plugins: postcssPlugin(env.pro),
                options: {
                    parser: scss
                }
            },
            autoprefixer: false,
            loaders: {
                css: ExtractTextPlugin.extract(['css?-url'])
            }
        },
        postcss: {
            plugins: postcssPlugin(env.pro),
            options: {
                parser: scss
            },
            autoprefixer: false,
            loaders: {
                css: ExtractTextPlugin.extract(['css?-url'])
            }
        }
    });

    proConfig.output.path = dist.js;

    // 生产环境插件
    proConfig.plugins.push(new webpack.DefinePlugin({
        'process.env': {
            NODE_ENV: '"production"'
        }
    }), new UglifyJsParallelPlugin({
        workers: os.cpus().length
    }));

    webpack(proConfig, (err, stats) => {
        if (err) {
            throw new gutil.PluginError('webpack', err);
        }
        gutil.log('[webpack compile]:', stats.endTime - stats.startTime, 'ms');
        done();
    });
});