sysolution/gulpfile.js
2024-04-02 17:38:33 +08:00

274 lines
8.9 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import gulp from "gulp";
import gulpIf from "gulp-if";
import MemoryFS from "memory-fs";
import syncBrowser from "browser-sync";
const browserSync = syncBrowser.create();
const __dirname = process.cwd();
// 创建内存文件系统实例
const mfs = new MemoryFS();
import htmlmin from "gulp-htmlmin"; // 压缩html文件
import uglify from "gulp-uglify"; // 压缩js文件
import babel from "gulp-babel"; // ES6转ES5
import sass from "gulp-sass"; // sass编译
import cleanCss from "gulp-clean-css"; // 压缩css文件
import replace from "gulp-replace"; // 替换文件内容
import ignore from "gulp-ignore"; // 排除文件不进入打包结果
import { deleteAsync } from "del"; // 清除打包目标目录
import fs from "fs/promises";
import path from "path";
import map from "map-stream";
import { createProxyMiddleware } from "http-proxy-middleware";
const devAssetsDir = "src";
const htmlGlob = `${devAssetsDir}/**/*.html`;
const cssGlob = `${devAssetsDir}/**/*.css`;
const jsGlob = `${devAssetsDir}/**/*.js`;
const staticGlob = `${devAssetsDir}/static/**/*`;
const modulesGlob = "modules/**/*";
const allFileGlob = `${devAssetsDir}/**/*`;
/**
* @type {"dev" | "prod"} 环境变量
*/
let env = "dev";
function setEnv(currEnv) {
return async () => {
env = currEnv;
};
}
import config from "./config.js";
let { moduleMap, assetsDir, devServer } = config;
let { browsersync: browsersyncOptions, httpProxy } = devServer && typeof devServer === "object" ? devServer : {};
moduleMap = parseModuleMap(moduleMap);
// 异步读取文件内容
async function getCode(path) {
return await fs.readFile(path, "utf8");
}
// 清除dist目录的任务
async function cleanTask() {
return deleteAsync([assetsDir]); // 使用 del 删除 dist 目录及其内容
}
export const clean = cleanTask;
/**
* @description 注入模块
* @param {NodeJS.ReadWriteStream} ctx
* @returns {NodeJS.ReadWriteStream}
*/
async function injectModules(ctx) {
// 遍历模块文件,将每个模块文件的内容替换到对应的模块标签中
for (const key in moduleMap) {
const filePath = path.join(__dirname, moduleMap[key]);
const code = await getCode(filePath);
const replaceStream = replace(key, code);
ctx = ctx.pipe(replaceStream);
}
return ctx;
}
// 制定html任务
async function htmlTask() {
const condition = env === "prod";
let ctx = gulp.src(htmlGlob, {
ignore: [staticGlob],
});
ctx = await injectModules(ctx);
return (
ctx
.pipe(
htmlmin({
removeComments: condition, // 生产环境下移除注释
collapseWhitespace: condition, // //压缩HTML
collapseBooleanAttributes: condition, //省略布尔属性的值 <input checked="true"/> ==> <input />
removeEmptyAttributes: env === "prod", //删除所有空格作属性值 <input id="" /> ==> <input />
removeScriptTypeAttributes: false, //删除<script>的type="text/javascript"
removeStyleLinkTypeAttributes: condition, //删除<style>和<link>的type="text/css"
minifyJS: condition, //压缩页面JS
minifyCSS: condition, //压缩页面CSS
})
)
.pipe(ignore.exclude(modulesGlob)) // 忽略模块文件
.pipe(gulpIf(condition, writeFileToFileSystem(assetsDir)))
// 将处理后的文件写入内存文件系统
.pipe(gulpIf(!condition, writeFileToMemory()))
);
}
// 定义Gulp任务
export const html = htmlTask;
// 制定js任务ES6转ES5再压缩js
async function jsTask() {
const ctx = gulp.src(jsGlob, {
ignore: [staticGlob],
});
const condition = env === "prod";
return ctx
.pipe(
gulpIf(
condition,
babel({
presets: ["@babel/preset-env"],
})
)
)
.pipe(
gulpIf(
condition,
uglify({
compress: {
drop_console: false, // 移除console语句
drop_debugger: true, // 移除debugger语句
},
})
)
)
.pipe(gulpIf(condition, writeFileToFileSystem(path.join(assetsDir))))
.pipe(gulpIf(!condition, writeFileToMemory()));
}
export const js = jsTask;
// css任务压缩css
async function cssTask() {
const ctx = gulp.src(cssGlob, {
ignore: [staticGlob],
});
const condition = env === "prod";
return ctx
.pipe(gulpIf(condition, cleanCss())) // css样式压缩
.pipe(gulpIf(condition, writeFileToFileSystem(path.join(assetsDir))))
.pipe(gulpIf(!condition, writeFileToMemory()));
}
export const css = cssTask;
// 静态文件目录,直接复制
async function staticFile() {
const condition = env === "prod";
const staticDir = "/static";
return gulp
.src(staticGlob)
.pipe(gulpIf(condition, writeFileToFileSystem(assetsDir)))
.pipe(gulpIf(!condition, writeFileToMemory(staticDir)));
}
export const staticFileTask = staticFile;
// 其他文件直接复制除了html, cssjs文件和static目录
async function otherFile() {
const condition = env === "prod";
return gulp
.src(allFileGlob, {
ignore: [htmlGlob, cssGlob, jsGlob, staticGlob],
})
.pipe(gulpIf(condition, writeFileToFileSystem(assetsDir)))
.pipe(gulpIf(!condition, writeFileToMemory()));
}
export const otherFileTask = otherFile;
// 打包任务清除dist目录然后并行执行html、js、css、其他文件、static等任务
export const build = gulp.series(setEnv("prod"), cleanTask, gulp.parallel(html, js, css, otherFileTask, staticFileTask));
// server任务开启一个本地服务器
async function serverTask() {
browsersyncOptions = browsersyncOptions && typeof browsersyncOptions === "object" ? browsersyncOptions : {};
const httpProxyList = Object.keys(httpProxy).map((prefix) => createProxyMiddleware(prefix, httpProxy[prefix]));
return browserSync.init({
...browsersyncOptions,
middleware: [
...httpProxyList,
// 自定义中间件来从内存文件系统中提供文件
function (req, res, next) {
let fileLogicPath = req.url.split("?")[0];
let url = fileLogicPath === "/" ? "/index.html" : fileLogicPath;
let filePath = path.join(__dirname, "/", devAssetsDir, ...url.split("/"));
mfs.readFile(decodeURIComponent(filePath), (err, data) => {
if (err) {
const map = {
ENOENT: () => console.error(`\x1b[31m找不到文件或目录${err.path}\x1b[0m`),
};
map[err.code] ? map[err.code]() : console.error(err);
res.statusCode = 404;
res.end(`not found ${req.url}`);
} else {
res.end(data);
}
});
},
],
server: true,
});
}
export const server = serverTask;
// 监听文件的修改,执行对应的任务
async function watchTask() {
gulp.watch(htmlGlob, gulp.series(html)).on("change", browserSync.reload);
gulp.watch(jsGlob, gulp.series(js)).on("change", browserSync.reload);
gulp.watch(cssGlob, gulp.series(css)).on("change", browserSync.reload);
gulp.watch(staticGlob, gulp.series(staticFileTask)).on("change", browserSync.reload);
gulp.watch(allFileGlob, gulp.series(otherFileTask)).on("change", browserSync.reload);
}
export const watch = watchTask;
// 开发服务
export const dev = gulp.series(setEnv("dev"), gulp.parallel(html, js, css, staticFileTask, otherFileTask), (cb) => setTimeout(() => cb(), 3000), server, watch);
/**
* 解析模块映射
* @param {Object} moduleMap
* @returns
*/
function parseModuleMap(moduleMap) {
const newModuleMap = {};
Object.keys(moduleMap).forEach((fileId) => {
const newId = `#include(${fileId})`;
newModuleMap[newId] = moduleMap[fileId];
});
return newModuleMap;
}
/**
* @description 把文件流写入到内存中
* @returns
*/
function writeFileToMemory() {
return map(function mapFile(file, cb) {
const filepath = path.resolve(file.path);
const dir = path.dirname(filepath);
if (!mfs.existsSync(dir)) {
mfs.mkdirpSync(dir);
}
file.contents && mfs.writeFileSync(filepath, file.contents);
cb(null, file);
});
}
/**
* @description 把文件流写入到硬盘中
* @param {String} assetsDir 写入到哪个目录如根目录的dist,则assetsDir位'dist'
* @returns
*/
function writeFileToFileSystem(assetsDir) {
return map(async function mapFile(file, cb) {
const relativePath = path.relative(path.join(process.cwd(), devAssetsDir), file.path);
const destFilePath = path.join(process.cwd(), assetsDir, relativePath);
const dirPath = path.dirname(destFilePath);
try {
if (file.contents) {
await fs.access(dirPath).catch(() => fs.mkdir(dirPath, { recursive: true }));
await fs.writeFile(destFilePath, file.contents);
}
} catch (error) {
console.log(error);
}
cb(null, file);
});
}