Light12306/DeployTools/Program.cs
2015-07-03 21:04:37 +08:00

434 lines
14 KiB
C#
Raw 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.

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)$" };
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();
}
}
}