Light12306/DeployTools/Program.cs

434 lines
14 KiB
C#
Raw Normal View History

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)
{
2014-09-02 21:41:17 +08:00
_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);
2014-09-02 21:41:17 +08:00
Console.WriteLine("使用压缩工具 {0}", _ajaxminiTool);
Console.WriteLine();
//1.创建目标目录
Console.WriteLine("正在准备目标目录....");
try
{
if (Directory.Exists(_destinationDirectory))
Directory.Delete(_destinationDirectory, true);
}
catch (Exception ex)
{
2014-09-02 21:41:17 +08:00
}
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);
2014-09-02 21:41:17 +08:00
//release tag
content = content.Replace("$BUILD_DATE$", DateTime.Now.ToString("yyyyMMddHHmmss"));
File.WriteAllText(fileFull, content);
2014-09-02 15:35:51 +08:00
//使用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();
2014-09-02 15:35:51 +08:00
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;
});
//插入新的脚本
2014-09-02 21:41:17 +08:00
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()
{
2014-09-02 21:41:17 +08:00
var include = new[] { @".*\.*?\.(jpg|gif|png|html|js|svg|eot|ttf|woff|otf|css|ico)$" };
var fileter = new[] { @"^(scripts|bin)\.*" };
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();
}
}
}