434 lines
14 KiB
C#
434 lines
14 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
|
||
namespace DeployTools
|
||
{
|
||
using System.Diagnostics;
|
||
using System.IO;
|
||
using System.Reflection;
|
||
using System.Security.Cryptography;
|
||
using System.Text.RegularExpressions;
|
||
|
||
using Newtonsoft.Json;
|
||
|
||
class Program
|
||
{
|
||
static string _projectRoot;
|
||
static string _sourceDirectory;
|
||
static string _destinationDirectory;
|
||
|
||
static string _ajaxminiTool;
|
||
|
||
static Dictionary<string, string> _sourceFiles;
|
||
static Dictionary<string, string> _moduleMap;
|
||
|
||
static void Main(string[] args)
|
||
{
|
||
_projectRoot = Path.GetFullPath(Path.Combine(Assembly.GetExecutingAssembly().Location, @"..\..\"));
|
||
_sourceDirectory = Path.Combine(_projectRoot, "Web12306");
|
||
_destinationDirectory = Path.Combine(_projectRoot, "wwwroot");
|
||
_ajaxminiTool = Path.Combine(_projectRoot, "BuildTools\\AjaxMinifier.exe");
|
||
_moduleMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||
|
||
Console.WriteLine("使用压缩工具 {0}", _ajaxminiTool);
|
||
Console.WriteLine();
|
||
|
||
//1.创建目标目录
|
||
Console.WriteLine("正在准备目标目录....");
|
||
try
|
||
{
|
||
if (Directory.Exists(_destinationDirectory))
|
||
Directory.Delete(_destinationDirectory, true);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
|
||
}
|
||
Directory.CreateDirectory(_destinationDirectory);
|
||
Console.WriteLine();
|
||
|
||
//2.创建文件列表
|
||
Console.WriteLine("正在扫描文件列表...");
|
||
CreateFileList();
|
||
Console.WriteLine("文件总数: {0}", _sourceFiles.Count);
|
||
Console.WriteLine();
|
||
|
||
//3.复制文件
|
||
Console.WriteLine("正在将文件复制到目标目录...");
|
||
CopySourceFiles();
|
||
|
||
//4.合并处理css文件
|
||
Console.WriteLine("正在合并样式表文件...");
|
||
ProcessCss("css\\index.css");
|
||
Console.WriteLine();
|
||
|
||
//5.处理js文件
|
||
ProcessScriptFiles();
|
||
|
||
//6.合并js文件
|
||
CombineScript();
|
||
|
||
//7.压缩html
|
||
ProcessHtml();
|
||
|
||
//8.写入记录
|
||
WriteModuleMap();
|
||
|
||
//9.清理目录
|
||
CleanupDirectory();
|
||
|
||
Console.WriteLine(_projectRoot);
|
||
Console.ReadKey();
|
||
}
|
||
|
||
static void CleanupDirectory()
|
||
{
|
||
Console.WriteLine("正在清理目标目录...");
|
||
|
||
foreach (var directory in Directory.GetDirectories(_destinationDirectory))
|
||
{
|
||
CleanupDirectory(directory);
|
||
}
|
||
|
||
Console.WriteLine("清理目标目录完成");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
static void CleanupDirectory(string path)
|
||
{
|
||
var directorys = Directory.GetDirectories(path);
|
||
foreach (var directory in directorys)
|
||
{
|
||
CleanupDirectory(directory);
|
||
}
|
||
|
||
if (Directory.GetDirectories(path).Length > 0 || Directory.GetFiles(path).Length > 0)
|
||
return;
|
||
Directory.Delete(path);
|
||
Console.WriteLine("\t已删除空目录 {0}", path);
|
||
}
|
||
|
||
static void WriteModuleMap()
|
||
{
|
||
Console.WriteLine("正在写入模块列表记录...");
|
||
|
||
var file = "modules.json";
|
||
var fullFile = Path.Combine(_projectRoot, file);
|
||
File.WriteAllText(fullFile, JsonConvert.SerializeObject(_moduleMap.Select(s => new
|
||
{
|
||
module = s.Key,
|
||
path = s.Value
|
||
}).ToArray()));
|
||
|
||
Console.WriteLine("模块列表记录完成");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
static void ProcessHtml()
|
||
{
|
||
Console.WriteLine("正在处理HTML页面文件...");
|
||
|
||
var htmlFiles = new[] { "index.html" };
|
||
foreach (var htmlFile in htmlFiles)
|
||
{
|
||
Console.Write("正在处理文件 {0} ...", htmlFile);
|
||
ProcessHtml(htmlFile);
|
||
Console.WriteLine("完成");
|
||
}
|
||
|
||
Console.WriteLine("HTML页面文件处理完成");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
static void ProcessHtml(string file)
|
||
{
|
||
var fileFull = Path.GetFullPath(Path.Combine(_destinationDirectory, file));
|
||
var content = File.ReadAllText(fileFull);
|
||
|
||
//处理模板
|
||
content = Regex.Replace(content, @"<script[^>]*?type=['""]?text/x-dot-template['""]?[^>]*?>[\w\W]+?</script>", _ =>
|
||
{
|
||
var tpl = _.Value;
|
||
return Regex.Replace(tpl, @"[\t\s\r\n]{2,}", " ");
|
||
}, RegexOptions.IgnoreCase);
|
||
//release tag
|
||
content = content.Replace("$BUILD_DATE$", DateTime.Now.ToString("yyyyMMddHHmmss"));
|
||
File.WriteAllText(fileFull, content);
|
||
|
||
|
||
//使用uglify 压缩
|
||
var psi = new ProcessStartInfo(System.Configuration.ConfigurationManager.AppSettings["minifyCmdPath"], "\"" + fileFull + "\"")
|
||
{
|
||
WindowStyle = ProcessWindowStyle.Hidden,
|
||
RedirectStandardOutput = true,
|
||
UseShellExecute = false,
|
||
StandardOutputEncoding = Encoding.UTF8
|
||
};
|
||
var p = Process.Start(psi);
|
||
content = p.StandardOutput.ReadToEnd();
|
||
|
||
|
||
|
||
File.WriteAllText(fileFull, content);
|
||
}
|
||
|
||
static void CombineScript()
|
||
{
|
||
Console.WriteLine("正在合并脚本文件...");
|
||
|
||
var targetHtml = "index.html";
|
||
var targetFullHtml = Path.Combine(_destinationDirectory, targetHtml);
|
||
var targetContent = File.ReadAllText(targetFullHtml);
|
||
|
||
var scripts = _sourceFiles.Keys.ToArray().Where(s => s.EndsWith(".js", StringComparison.OrdinalIgnoreCase)).ToList();
|
||
//处理script标签
|
||
var allScriptTags = new List<string>();
|
||
targetContent = Regex.Replace(targetContent, @"<script.*?src=['""]?([^\s'"">]+?)['""\s>].*?</script>", _ =>
|
||
{
|
||
allScriptTags.Add(Path.GetFullPath(Path.Combine(_destinationDirectory, _.Groups[1].Value)));
|
||
return string.Empty;
|
||
});
|
||
//插入新的脚本
|
||
targetContent = targetContent.Replace("</body>", "<script src=\"js/lib.js?v" + DateTime.Now.ToString("yyyyMMddHHmmss") + "\"></script>");
|
||
File.WriteAllText(targetFullHtml, targetContent);
|
||
|
||
//合并脚本库
|
||
var extLibs = allScriptTags.Take(allScriptTags.Count - 1).ToArray();
|
||
var extLibContent = string.Join(";", extLibs.Select(s =>
|
||
{
|
||
var path = Path.Combine(_destinationDirectory, s);
|
||
var content = File.ReadAllText(path);
|
||
File.Delete(path);
|
||
return content;
|
||
}));
|
||
//页面脚本
|
||
var scriptsHash = new HashSet<string>(allScriptTags, StringComparer.OrdinalIgnoreCase);
|
||
var pageLibContent = string.Join(";", scripts.Select(s =>
|
||
{
|
||
var path = Path.GetFullPath(Path.Combine(_destinationDirectory, s));
|
||
if (scriptsHash.Contains(path))
|
||
return string.Empty;
|
||
|
||
var content = File.ReadAllText(path);
|
||
File.Delete(path);
|
||
return content;
|
||
}).Where(s => !string.IsNullOrEmpty(s)));
|
||
|
||
//引导脚本
|
||
var bootScriptFile = Path.GetFullPath(Path.Combine(_destinationDirectory, allScriptTags.Last()));
|
||
var bootScript = File.ReadAllText(bootScriptFile);
|
||
File.Delete(bootScriptFile);
|
||
|
||
var extLibFile = "js/lib.js";
|
||
var extLibFullFile = Path.Combine(_destinationDirectory, extLibFile);
|
||
|
||
|
||
File.WriteAllText(extLibFullFile, string.Join(";", new[] { extLibContent, pageLibContent, bootScript }));
|
||
|
||
Console.WriteLine("脚本文件合并完成");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
static void ProcessScriptFiles()
|
||
{
|
||
Console.WriteLine("正在处理脚本文件...");
|
||
foreach (var file in _sourceFiles.Where(s => s.Key.EndsWith(".js", StringComparison.OrdinalIgnoreCase)))
|
||
{
|
||
RewriteModuleId(file.Key);
|
||
|
||
//压缩
|
||
CompressScript(file.Key);
|
||
}
|
||
Console.WriteLine("脚本文件处理完成");
|
||
Console.WriteLine();
|
||
}
|
||
|
||
static void CompressScript(string filepath)
|
||
{
|
||
Console.Write("正在压缩 " + filepath + "...");
|
||
filepath = Path.GetFullPath(Path.Combine(_destinationDirectory, filepath));
|
||
Process.Start(new ProcessStartInfo(_ajaxminiTool, "-comments:none -esc:true \"" + filepath + "\" -out \"" + filepath + "\"")
|
||
{
|
||
WindowStyle = ProcessWindowStyle.Hidden
|
||
}).WaitForExit();
|
||
|
||
Console.WriteLine("压缩完成");
|
||
}
|
||
|
||
static void RewriteModuleId(string filepath)
|
||
{
|
||
Console.Write("正在处理文件 {0}", filepath);
|
||
|
||
filepath = Path.GetFullPath(Path.Combine(_destinationDirectory, filepath));
|
||
var folder = Path.GetDirectoryName(filepath);
|
||
|
||
var content = File.ReadAllText(filepath);
|
||
|
||
//是否是模块?
|
||
var depList = new List<string>();
|
||
if (Regex.IsMatch(content, @"^[\r\n\s]*(define\()\s*(function\s*\()", RegexOptions.IgnoreCase))
|
||
{
|
||
var hash = GetModuleId(filepath);
|
||
Console.WriteLine("正在处理...");
|
||
Console.WriteLine("\t模块ID:{0}", hash);
|
||
|
||
//替换内容,并获得所有的模块ID
|
||
content = Regex.Replace(content, @"([\r\n=\s]require\s*\(\s*['""])([^'""]+)(['""]\))", _ =>
|
||
{
|
||
var path = Path.GetFullPath(Path.Combine(folder, _.Groups[2].Value));
|
||
var id = GetModuleId(path);
|
||
depList.Add(id);
|
||
Console.WriteLine("\t依赖模块ID:{0}", id);
|
||
return _.Groups[1].Value + id + _.Groups[3].Value;
|
||
}, RegexOptions.IgnoreCase);
|
||
|
||
content = Regex.Replace(content, @"^[\r\n\s]*(define\()\s*(function\s*\()", _ => _.Groups[1].Value + "\"" + hash + "\",[" + string.Join(",", depList.Select(s => "\"" + s + "\"")) + "]," + _.Groups[2].Value, RegexOptions.IgnoreCase);
|
||
}
|
||
else
|
||
{
|
||
Console.WriteLine("非模块,已跳过");
|
||
}
|
||
content = Regex.Replace(content, @"(seajs\.use\s*\(\s*['""])([^'""]+)(['""]\))", _ =>
|
||
{
|
||
var path = Path.GetFullPath(Path.Combine(folder, _.Groups[2].Value));
|
||
var id = GetModuleId(path);
|
||
depList.Add(id);
|
||
Console.WriteLine("\t依赖模块ID:{0}", id);
|
||
return _.Groups[1].Value + id + _.Groups[3].Value;
|
||
}, RegexOptions.IgnoreCase);
|
||
|
||
File.WriteAllText(filepath, content);
|
||
Console.WriteLine("已处理完成");
|
||
}
|
||
|
||
static string GetModuleId(string path)
|
||
{
|
||
if (!path.EndsWith(".js", StringComparison.OrdinalIgnoreCase))
|
||
path += ".js";
|
||
|
||
if (_moduleMap.ContainsKey(path))
|
||
return _moduleMap[path];
|
||
|
||
var hash = string.Join("", MD5CryptoServiceProvider.Create().ComputeHash(Encoding.UTF8.GetBytes(path.ToLower())).Select(s => s.ToString("X2")).ToArray());
|
||
_moduleMap.Add(path, hash);
|
||
|
||
return hash;
|
||
}
|
||
|
||
static void ProcessCss(string cssfile)
|
||
{
|
||
Console.Write("正在处理 " + cssfile + "...");
|
||
var content = ImportCssFile(cssfile);
|
||
var fullPath = Path.Combine(_destinationDirectory, cssfile);
|
||
|
||
//corrent reference
|
||
content = Regex.Replace(content, @"url\(['""]?(?!data:)([^?'""]+?)(\?[^'""\)]*?)?['""]?\)", _ =>
|
||
{
|
||
var extag = _.Groups[2].Success ? _.Groups[2].Value : "";
|
||
return "url(\"" + GetRelativePath(Path.GetDirectoryName(cssfile), GetTargetSubPath(_.Groups[1].Value)).Replace('\\', '/').ToLower() + extag + "\")";
|
||
}, RegexOptions.IgnoreCase);
|
||
|
||
File.WriteAllText(Path.Combine(_destinationDirectory, cssfile), content);
|
||
Console.WriteLine("合并完成");
|
||
|
||
Console.Write("正在压缩 " + cssfile + "...");
|
||
Process.Start(new ProcessStartInfo(_ajaxminiTool, "-comments:none \"" + fullPath + "\" -out \"" + fullPath + "\"")
|
||
{
|
||
WindowStyle = ProcessWindowStyle.Hidden
|
||
}).WaitForExit();
|
||
|
||
Console.WriteLine("压缩完成");
|
||
}
|
||
|
||
static string GetRelativePath(string basePath, string secondPath)
|
||
{
|
||
if (string.IsNullOrEmpty(secondPath) || string.IsNullOrEmpty(basePath))
|
||
return secondPath;
|
||
|
||
var ps1 = basePath.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
|
||
var ps2 = secondPath.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
var upperBound = Math.Min(ps1.Length, ps2.Length);
|
||
var startIndex = Enumerable.Range(0, upperBound).FirstOrDefault(s => string.Compare(ps1[s], ps2[s], true) != 0);
|
||
|
||
if (startIndex == 0)
|
||
return "/" + secondPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||
if (ps1.Length == startIndex && ps2.Length <= startIndex)
|
||
return "./";
|
||
|
||
return string.Join(Path.AltDirectorySeparatorChar.ToString(), Enumerable.Repeat("..", ps1.Length - startIndex).Concat(ps2.Skip(startIndex)).ToArray());
|
||
}
|
||
|
||
static string GetProjectSubPath(string path)
|
||
{
|
||
var removeLength = _sourceDirectory.EndsWith("\\") ? _sourceDirectory.Length : _sourceDirectory.Length + 1;
|
||
return path.Remove(0, removeLength);
|
||
}
|
||
|
||
static string GetTargetSubPath(string path)
|
||
{
|
||
var removeLength = _destinationDirectory.EndsWith("\\") ? _destinationDirectory.Length : _destinationDirectory.Length + 1;
|
||
return path.Remove(0, removeLength);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 修正CSS文件引用路径
|
||
/// </summary>
|
||
/// <param name="filePath">要修正的css文件路径</param>
|
||
/// <returns></returns>
|
||
static string ImportCssFile(string filePath)
|
||
{
|
||
var fullPath = Path.Combine(_destinationDirectory, filePath);
|
||
var content = File.ReadAllText(fullPath);
|
||
var directory = Path.GetDirectoryName(fullPath);
|
||
|
||
File.Delete(fullPath);
|
||
|
||
//import
|
||
content = Regex.Replace(content, @"@import\s+url\(['""]?(?!data:)([^?'""]+?)(\?[^'""\)]*?)?['""]?\);?", _ => ImportCssFile(Path.GetFullPath(Path.Combine(directory, _.Groups[1].Value.Replace('/', '\\')))), RegexOptions.IgnoreCase);
|
||
|
||
//reference
|
||
content = Regex.Replace(content, @"url\(['""]?(?!data:)([^?'""]+?)(\?[^'""\)]*?)?['""]?\)", _ =>
|
||
{
|
||
var extag = _.Groups[2].Success ? _.Groups[2].Value : "";
|
||
var path = _.Groups[1].Value.Replace('/', '\\');
|
||
if (path[0] == '\\')
|
||
path = Path.Combine(_destinationDirectory, path.Remove(0, 1));
|
||
else
|
||
path = Path.Combine(directory, path);
|
||
return string.Format("url(\"{0}{1}\")", Path.GetFullPath(path), extag);
|
||
}, RegexOptions.IgnoreCase);
|
||
|
||
return content;
|
||
}
|
||
|
||
static void CreateFileList()
|
||
{
|
||
var include = new[] { @".*\.*?\.(jpg|gif|png|html|js|svg|eot|ttf|woff|otf|css|ico|ogg|woff2)$" };
|
||
var fileter = new[] { @"^(scripts|bin|app_data)\.*" };
|
||
|
||
var allfiles = Directory.GetFiles(_sourceDirectory, "*.*", SearchOption.AllDirectories).ToDictionary(GetProjectSubPath, StringComparer.OrdinalIgnoreCase);
|
||
|
||
//过滤
|
||
_sourceFiles = allfiles.Where(s => include.Any(y => Regex.IsMatch(s.Key, y, RegexOptions.IgnoreCase)) && !fileter.Any(y => Regex.IsMatch(s.Key, y, RegexOptions.IgnoreCase)))
|
||
.ToDictionary(s => s.Key, s => s.Value, StringComparer.OrdinalIgnoreCase);
|
||
}
|
||
|
||
static void CopySourceFiles()
|
||
{
|
||
foreach (var file in _sourceFiles)
|
||
{
|
||
Console.WriteLine("\t正在复制:{0}", file.Key);
|
||
|
||
var target = Path.Combine(_destinationDirectory, file.Key).ToLower();
|
||
Directory.CreateDirectory(Path.GetDirectoryName(target));
|
||
File.Copy(file.Value, target);
|
||
}
|
||
Console.WriteLine("复制完成");
|
||
Console.WriteLine();
|
||
}
|
||
}
|
||
}
|