
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
sk2-gulp-cli
Advanced tools
[PROJECT HOME](https://gitee.com/caiyue823/sk2-gulp-cli) ## Intro A workflow cli with built-in gulp-cli & gulpfile & flexible config
一个内置gulp-cli & gulpfile & 灵活配置的工作流cli
└── sk2-gulp-cli ······项目根目录
├─ bin ·············node命令目录
│ ├─ index.js ·····bin入口文件
├─ lib ·············gulp相关文件
│ ├─ cmdPromise.js ····将node-cmd.run函数包装成Promise函数exec
│ ├─ config.js ····gulefile相关配置,主要为路径默认配置
│ ├─ data.js ······页面相关默认配置
│ ├─ gulpfile.js ··主gulpfile文件
│ ├─ index.js ·····导出gulefile,main入口文件
├─ .eslintrc.js ····eslint默认配置,可以被项目的eslint配置覆盖
├─ .gitignore ······git忽略文件配置
├─ .npmrc ··········npm镜像下载地址
├─ LICENSE ········证书
├─ lint.yml ········暂不使用
├─ package.json ····npm包说明文件
├─ project.config.js ····项目默认配置样本,为lib中config和data的合集。
本项目中无用,需在使用项目中添加
├─ README.md ·······项目说明
###安装
npm install sk2-gulp-cli -D 或 yarn add sk2-gulp-cli -Dsk2-gulp-cli taskName, 可用命令如下###命令
sk2-gulp-cli lintscss/js文件lint检查
sk2-gulp-cli compilescss/js/html文件编译。编译后的css/js/html会被放入temp文件夹中。
sk2-gulp-cli serve使用内置服务器预览、监听、调试代码。编译后的css/js/html会被放入temp文件夹中。
open: 是否在启动服务器时打开浏览器窗口, 默认: falseport: 设置端口号, 默认: 2080sk2-gulp-cli build打包项目并将文件放入dist目录. 推荐使用production模式对代码/资源进行压缩以达到最佳效果。
production: production模式, 默认: falseprod: 等同于productionsk2-gulp-cli start使用内置服务器预览production模式打包的项目以获取真实的线上浏览体验。
这个命令其实是sk2-gulp-cli serve --prod的别名。
注意:在这个模式下,源文件监听/调试将被关闭。如果你想要调试,请使用sk2-gulp-cli serve
open: 是否在启动服务器时打开浏览器窗口, 默认: falseport: 设置端口号, 默认: 2080sk2-gulp-cli deploy将打包的dist文件夹push到git仓库
branch: 要push的分支名, 默认: 'gh-pages'sk2-gulp-cli clean清空dist & temp文件
你可以通过在自己的项目根目录新建一个project.config.js,根据自己的需要来覆盖这些配置。 默认配置如下。项目根目录提供了一份project.config.js,可以直接复制它到你的项目根目录进行修改即可。
module.exports = {
data: {
menus: [
{
name: 'Home',
icon: 'aperture',
link: 'index.html'
},
{
name: 'Features',
link: 'features.html'
},
{
name: 'About',
link: 'about.html'
},
{
name: 'Contact',
link: '#',
children: [
{
name: 'Twitter',
link: 'https://twitter.com/w_zce'
},
{
name: 'About',
link: 'https://weibo.com/zceme'
},
{
name: 'divider'
},
{
name: 'About',
link: 'https://github.com/zce'
}
]
}
],
pkg: require('./package.json'),
date: new Date(),
},
config: {
SRC: 'src',
DIST: 'dist',
TEMP: '.tmp',
PUBLIC: 'public',
PATHS: {
style: 'assets/styles/*.scss',
script: 'assets/scripts/*.js',
page: '*.html',
image: 'assets/images/**',
font: 'assets/fonts/**',
}
}
}
lint任务分为styleLint和scriptLint两个子任务,任务的实现相对简单, 分别定义两个任务,引入对应的lint插件,调用相应api即可。以下是scriptLint的实现
const scriptLint = () => {
return src(PATHS.script, srcConfig)
//执行lint
.pipe(plugins.eslint())
//输出lint结果至控制台
.pipe(plugins.eslint.format())
// 如果报错终止执行
.pipe(plugins.eslint.failAfterError())
}
在使用eslint插件时需注意的是,当手动传入参数至gulp-eslint插件时,插件的参数格式与.eslintrc.js不匹配。 例如,gulp-eslint插件的envs,globals属性均为数组形式,而.eslintrc.js对应的属性为对象。 因此更好的方式应该是不处理参数,让插件在运行时自动读取运行时项目根目录的.eslintrc.js
const lint = parallel(styleLint, scriptLint);
在实现lint命令时使用parallel并行执行,一是缩短时间,二是因为任何一项lint任务一旦报错都无需往下继续。 当然这样做也有一个缺点,子任务的报错日志会交替输出,阅读上有点麻烦。
compile任务分为css/js/html三个子任务,输出半成品的css/js/html文件,配合useref/watcher任务使用。 以js任务为例,实现如下
// script编译
const script = () => {
return src(PATHS.script, srcConfig)
.pipe(plugins.babel({
// 使用require引用,当变成cli时,require找到的是当前cli下的node_modules里的包
presets: [require('@babel/preset-env')]
}))
.pipe(dest(TEMP))
// 与watcher中的watch任务配合,实现浏览器刷新
.pipe(server.reload({stream: true}))
}
在实现compile子任务时需要注意以下几点
temp而不是dist目录。这样做有以下几点原因半成品文件,它们并不具备在生产环境运行的条件,比如生成的js和css文件没有经过压缩,而且没有生成依赖库文件(vendor.css/js),
html文件在没有经过useref处理时也没有真正生成对应的依赖库引用。因此它们不能被放入意味着生产的dist包里半成品可以配合useref任务生成真正的生产环境代码。而在使用useref时,
从temp目录读取半成品文件,然后经过处理写入dist,可以避免读写冲突server.reload// 编译,并行执行
const compile = parallel(style, script, page);
build任务的实现如下
// 打包命令,需要先clean,然后并行执行(css/js/html的编译与引用查找,以及其他静态资源的压缩)
const build = series(clean, parallel(series(compile, useref), image, font, extra));
由于build需要区分开发/生产模式,因此在useref/image/font等子任务中均需要做相应处理(不包括compile)。 以下代码通过插件从命令行中获取isProd。当isProd为true时, 需要对所有的资源进行压缩/混淆/查找引用等处理
// 转化命令行参数为一个对象
const args = require('node-args-parser')(process.argv);
console.log('args', args);
// 处理相关命令行参数
// 提取prod参数供后续使用
const isProd = args['-production'] || args['-prod'] || false;
其中useref的实现如下
const useref = () => {
return src(PATHS.page, tempConfig)
// tempConfig中的cwd:TEMP会将useref的工作目录改为TEMP,
// 加上..才是项目根目录,从而找到node_modules
.pipe(plugins.useref({searchPath: [TEMP, '.', '..']}))
// 仅在prod模式下进行相应文件压缩
.pipe(plugins.if(isProd && /\.css$/, plugins.cleanCss()))
.pipe(plugins.if(isProd && /\.js$/, plugins.uglify()))
.pipe(plugins.if(isProd && /\.html$/, plugins.htmlmin({
collapseWhitespace: true,
removeComments: true,
minifyJS: true,
minifyCSS: true,
})))
.pipe(dest(DIST));
}
image任务
const image = () => {
return src(PATHS.image, srcConfig)
// 仅在prod模式下进行压缩
.pipe(plugins.if(isProd, plugins.imagemin()))
.pipe(dest(DIST));
}
实现serve时需要考虑以下两点
这是开发环境下的serve,由compile和watcher任务组成
// 开发模式下的serve,编译加watcher即可
const serveDev = series(compile, watcher);
这是生产环境下的serve,由compile和watcher任务组成
// prod模式下的serve,需要先打包,然后watcher即可。
// 这时候watcher中不监听任何文件变化,相关逻辑在watcher中处理
const serveProd = series(build, watcher);
在watcher任务中根据isProd处理相关逻辑,代码如下
const watcher = () => {
// 开发模式下监听文件变化;prod模式不监听
if (!isProd) {
// 监听相关css/js/html文件,并重新执行对应的编译
watch(PATHS.style, srcConfig, style);
watch(PATHS.script, srcConfig, script);
watch(PATHS.page, srcConfig, page);
// 监听图片/字体/其他静态资源文件,刷新浏览器
watch([PATHS.image, PATHS.font], srcConfig, server.reload);
watch('**', publicConfig, server.reload);
}
const serverCfg = {
// prod模式只使用dist目录
// 开发模式下,需使用temp里的css/js/html文件,src下的图片/字体文件,public下的其他静态资源文件
baseDir: isProd ? [DIST] : [TEMP, SRC, PUBLIC],
// prod模式下,不需要任何路由;开发模式下,需要通过路由找到/node_modules下的vendor文件
routes: !isProd && {
'/node_modules': 'node_modules',
}
}
server.init({
// 优先使用命令行参数的设置
port: args['-port'] || 2080,
open: args['-open'] || false,
server: {
...serverCfg,
},
})
}
而最后向外暴露出的serve命令如下
// 对外暴露的serve命令,根据命令行prod参数执行不同的serve
const serve = isProd ? serveProd : serveDev;
deploy的目标是将打包的dist文件夹git push到github上。这个任务很有意思, 个人思路其实很简单,通过&&连接命令来执行。但中间经过了几轮试错,在这里记录一下过程
function gitPush(branchName = 'gh-pages') {
//git add foldername
//git commit -m "commit operation"
process.argv.push('deploy');
process.argv.push('&&');
process.argv.push('git');
process.argv.push('add');
process.argv.push('.');
const args = require('node-args-parser')(process.argv);
const gulpfilePath = require.resolve('..');
const cmd = require('node-cmd');
const cwd = process.cwd();
process.argv.push('--gulpfile');
process.argv.push(gulpfilePath);
process.argv.push('--cwd');
process.argv.push(cwd);
handleDeploy();
function handleDeploy() {
// 如果非deploy命令,走require方式执行
if (process.argv[2] !== 'deploy') {
require('gulp/bin/gulp');
return;
}
const branchName = args['-branch'] || 'gh-pages';
console.log(gulpfilePath, cwd);
// 如果是deploy,为了保证命令的先后执行,需要在这里手动执行gulp
cmd.run(`gulp deploy --gulpfile ${gulpfilePath} --cwd ${cwd}`, (e, data) => {
if (e) {
console.log(e);
return;
}
console.log(data);
cmd.run('git add .', (e) => {
if (e) {
console.log(e);
...省略
// 将cmd run方法包装成一个Promise方法exec
const cmd = require('node-cmd');
function exec(line) {
return new Promise(function (resolve, reject) {
cmd.run(line, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
})
})
}
module.exports = {
exec,
}
// 使用node-cmd插件异步执行git cmd
const git = (done) => {
return exec(`git add ./${DIST}`)
.then((data) => {
console.log('git add success', data);
return exec(`git commit -m "commit ${DIST} ${new Date().toLocaleString()}"`)
})
.then((data) => {
console.log('git commit success', data);
return exec(`git push origin ${args['-branch'] || 'gh-pages'}`)
})
.then((data) => {
console.log('git push success', data);
})
.catch(e => {
console.log(e);
done(false);
})
}
// deploy命令,先打包,再git
const deploy = series(build, git);
整体gulpfile实现思路如上,具体代码及详细注释请参考lib/gulpfile.js
实现如下
config.js和data.js分别为gulpfile的相关配置文件,而index.js只是对gulpfile.js 做了一个导出 依次来看
// 默认的config参数,可以被项目根目录的project.config.js里的config对象覆盖
module.exports = {
SRC: 'src',
DIST: 'dist',
TEMP: 'temp',
PUBLIC: 'public',
PATHS: {
style: 'assets/styles/*.scss',
script: 'assets/scripts/*.js',
page: '*.html',
image: 'assets/images/**',
font: 'assets/fonts/**',
}
}
// 默认的data参数,可以被项目根目录的project.config.js里的data对象覆盖
module.exports = {
menus: [...省略],
//因为被放在lib下,需要往上一级找到package.json
pkg: require('../package.json'),
date: new Date(),
}
// 获取命令行执行目录
const CWD = process.cwd();
// 设置配置文件名称
const configFileName = 'project.config.js';
// 读取gulp 编译相关默认配置
let cfg = {
data: require('./data'),
config: require('./config')
};
try {
const projectCfg = require(resolve(CWD, configFileName));
cfg = Object.assign({}, cfg, projectCfg);
} catch (e) {
console.log(`read ${configFileName} error`);
}
module.exports = require('./gulpfile');
#!/usr/bin/env node
/**
* 通过往命令行参数里添加--gulpfile,将运行项目的gulpfile指向当前cli包的lib/gulpfile
* 通过往命令行参数里添加--cwd,并将运行项目的cwd传入,以防止gulp将运行目录重置为当前cli包目录
*/
process.argv.push('--gulpfile');
// require.resolve仅解析当前文件路径,并不执行当前文件代码,通过入参..的方式找到上一级目录(cli包目录)。
// 此时根据node查找模块的机制,会找到包目录的package.json里的main字段,从而定位到lib下的index文件
process.argv.push(require.resolve('..'));
process.argv.push('--cwd');
process.argv.push(process.cwd());
// console.log(process.argv);
// 这里使用了npm查找包的机制,会找到cli包下的node_modules里的gulp
// 而gulp中执行了require('gulp-cli')(),真正去执行gulpfile.js
require('gulp/bin/gulp');
{"files": ["bin","lib"]}
FAQs
[PROJECT HOME](https://gitee.com/caiyue823/sk2-gulp-cli) ## Intro A workflow cli with built-in gulp-cli & gulpfile & flexible config
We found that sk2-gulp-cli demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.