/// <summary> /// Adds all files in a single directory to a fileListsByAssetType /// </summary> /// <param name="fileListsByAssetType"></param> /// <param name="dirPath"></param> private void AddRequiredFilesSingleDirectory(FileListsByAssetType fileListsByAssetType, AssetPath dirPath) { string absolutePath = dirPath.AbsolutePath; IEnumerable<string> filePaths = Directory.EnumerateFiles(absolutePath); // Need to first process the nuspec files, and then the asset files. // This because the asset files may depend on the files pointed at by the nuspec files. for (int i = 0; i < 2; i++) { foreach (string filePath in filePaths) { if (i == 0) { if (NuspecFile.IsNuspecFile(filePath)) { fileListsByAssetType.Append(GetDependencies(filePath, dirPath)); } } else { AssetType? assetType = AssetTypeOfFile(filePath); if (assetType != null) { fileListsByAssetType.Add(dirPath.AbsolutePathToAssetPath(filePath), assetType.Value); } } } } }
/// <summary> /// Adds a directory with assets (script files, etc.) to the stored list of asset directories. /// </summary> /// <param name="dirPath"></param> public static void AddAssetDirectory(AssetPath dirPath) { List<AssetPath> assetDirectoryList = (List<AssetPath>)HttpContext.Current.Items[AssetDirectoryItemKey]; if (assetDirectoryList == null) { assetDirectoryList = new List<AssetPath>(); HttpContext.Current.Items[AssetDirectoryItemKey] = assetDirectoryList; } assetDirectoryList.Add(dirPath); }
/// <summary> /// Same as GetRequiredFilesForDirectory, but uncached. /// </summary> /// <param name="dirPath"> /// Path to the directory. /// </param> /// <returns></returns> private FileListsByAssetType GetRequiredFilesForDirectoryUnchached(AssetPath dirPath) { FileListsByAssetType fileListsByAssetType = new FileListsByAssetType(); // Note: parentDirs will contain dirPath itself. // // dirPath.ParentDirs will give you something like // ~/Views/Shared/EditorTemplates/HomeAddress // ~/Views/Shared/EditorTemplates // ~/Views/Shared // However, the longer directory tends to have the more specific files. If there is a dependency between files in these // directories, it would be from more specific to less specific, not the other way around. So process the directories in // reverse order, so CSS and JS files in more common directories are loaded first. IEnumerable <AssetPath> parentDirs = dirPath.ParentDirs("Views").Reverse(); foreach (AssetPath parentDir in parentDirs) { AddRequiredFilesSingleDirectory(fileListsByAssetType, parentDir); } return(fileListsByAssetType); }
/// <summary> /// Takes a file path and returns a string formatted like this: /// /// * files with no Area, with controller Shared, and that live in a directory with a name starting with _Layout: /// @___@ /// /// * files with no Area, with controller Shared, but don't live in a directory with a name starting with _Layout: /// @___@@ /// /// * files with an Area, with controller Shared, and that live in a directory with a name starting with _Layout: /// [Area]___@ /// /// * files with an Area, with controller Shared, but don't live in a directory with a name starting with _Layout: /// [Area]___@@ /// /// * all other files, with no area /// @___[Controller] /// /// * all other files, with an area /// [Area]___[Controller] /// /// Note that alphabetically, @ sorts before any letter or digit. /// Also, @___@@ sorts after @___@. /// /// The method assumes that the file path is root relative, and follows MVC conventions: /// /// * path with an Area: /// ~/Areas/TestArea/Views/home/index/indexspecific.css /// /// * path without an Area: /// ~/Views/home/index/indexspecific.css /// /// If the file path doesn't adhere to this, the method returns /// @ /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static string FilePathSortKey(AssetPath assetPath) { string filePath = assetPath.RootRelativePath; if (!filePath.StartsWith("~/")) { throw new Exception(string.Format("FilePathSortKey - filePath {0} does not start with ~/", filePath)); } string[] filePathComponents = filePath.Split(new char[] { '/' }); // Note that the first element in filePathComponents will be the ~ // The last element is the file name itself. string area = "@"; int nbrComponents = filePathComponents.Length; int controllerIdx = 2; if (string.CompareOrdinal(filePathComponents[1], "Areas") == 0) { if ((nbrComponents < 6) || (string.CompareOrdinal(filePathComponents[3], "Views") != 0)) { return "@"; } area = filePathComponents[2]; controllerIdx = 4; } else if (string.CompareOrdinal(filePathComponents[1], "Views") == 0) { if (nbrComponents < 4) { return "@"; } } else { return "@"; } string controller = filePathComponents[controllerIdx]; if (string.CompareOrdinal(controller, "Shared") == 0) { controller = "@"; } string postfix = "@"; int postControllerDirectoryIdx = (controllerIdx + 1); if (nbrComponents > postControllerDirectoryIdx) { string postControllerDirectory = filePathComponents[postControllerDirectoryIdx].ToLower(); if (postControllerDirectory.StartsWith("_layout")) { postfix = ""; } } string sortKey = area + "___" + controller + postfix; return sortKey; }
/// <summary> /// Adds all files in a single directory to a fileListsByAssetType /// </summary> /// <param name="fileListsByAssetType"></param> /// <param name="dirPath"></param> private void AddRequiredFilesSingleDirectory(FileListsByAssetType fileListsByAssetType, AssetPath dirPath) { string absolutePath = dirPath.AbsolutePath; string[] filePaths = Directory.EnumerateFiles(absolutePath).ToArray(); // Need to first process the nuspec files, and then the asset files. // This because the asset files may depend on the files pointed at by the nuspec files. for (int i = 0; i < 2; i++) { foreach (string filePath in filePaths) { if (i == 0) { if (NuspecFile.IsNuspecFile(filePath)) { fileListsByAssetType.Append(GetDependencies(filePath, dirPath)); } } else { AssetType?assetType = AssetTypeOfFile(filePath); if (assetType != null) { fileListsByAssetType.Add(dirPath.AbsolutePathToAssetPath(filePath), assetType.Value); } } } } }
/// <summary> /// Takes the path to a directory and returns the files in that directory, the files in the parent directories (down to the Views or ~ dir), /// and all the files /// that that directory depends on (via .nuspec files). /// /// It does not go into sub directories. /// /// Uses caching to reduce trips to the file system. /// </summary> /// <param name="dirPath"> /// Path to the directory. /// </param> /// <returns> /// The required files, split by asset type. /// </returns> public FileListsByAssetType GetRequiredFilesForDirectory(AssetPath dirPath) { string absolutePath = dirPath.AbsolutePath; var fileListsByAssetType = _cacheHelper.Get(absolutePath, () => GetRequiredFilesForDirectoryUnchached(dirPath), new[] { absolutePath }); return fileListsByAssetType; }
/// <summary> /// Same as GetRequiredFilesForDirectory, but uncached. /// </summary> /// <param name="dirPath"> /// Path to the directory. /// </param> /// <returns></returns> private FileListsByAssetType GetRequiredFilesForDirectoryUnchached(AssetPath dirPath) { FileListsByAssetType fileListsByAssetType = new FileListsByAssetType(); // Note: parentDirs will contain dirPath itself. // // dirPath.ParentDirs will give you something like /// ~/Views/Shared/EditorTemplates/HomeAddress /// ~/Views/Shared/EditorTemplates /// ~/Views/Shared // However, the longer directory tends to have the more specific files. If there is a dependency between files in these // directories, it would be from more specific to less specific, not the other way around. So process the directories in // reverse order, so CSS and JS files in more common directories are loaded first. IEnumerable<AssetPath> parentDirs = dirPath.ParentDirs("Views").Reverse(); foreach (AssetPath parentDir in parentDirs) { AddRequiredFilesSingleDirectory(fileListsByAssetType, parentDir); } return fileListsByAssetType; }
/// <summary> /// Reads the dependencies in a Nuspec file. These are directories. /// Accumulates the assets in those directories to in a FileListsByAssetType. /// This is then returned. /// /// This method calls the dependency resolver concurrently. /// </summary> /// <param name="absoluteNuspecPath"> /// Path of the nuspec file /// </param> /// <param name="nuspecFileDirPath"> /// The directory where the nuspec file is located. /// </param> /// <returns></returns> private FileListsByAssetType GetDependencies(string absoluteNuspecPath, AssetPath nuspecFileDirPath) { FileListsByAssetType fileListsByAssetType = new FileListsByAssetType(); var nuspecFile = new NuspecFile(absoluteNuspecPath); List<AssetPath> dependencyAssetPaths = nuspecFile.DependencyIds.Select(d => nuspecFileDirPath.Create(d)).ToList(); // Call the dependency resolver concurrently for each dependency foreach (AssetPath dependencyAssetPath in dependencyAssetPaths) { fileListsByAssetType.Append(GetRequiredFilesForDirectory(dependencyAssetPath)); } return fileListsByAssetType; }
/// <summary> /// Takes a file path and returns a string formatted like this: /// /// * files with no Area, with controller Shared, and that live in a directory with a name starting with _Layout: /// @___@ /// /// * files with no Area, with controller Shared, but don't live in a directory with a name starting with _Layout: /// @___@@ /// /// * files with an Area, with controller Shared, and that live in a directory with a name starting with _Layout: /// [Area]___@ /// /// * files with an Area, with controller Shared, but don't live in a directory with a name starting with _Layout: /// [Area]___@@ /// /// * all other files, with no area /// @___[Controller] /// /// * all other files, with an area /// [Area]___[Controller] /// /// Note that alphabetically, @ sorts before any letter or digit. /// Also, @___@@ sorts after @___@. /// /// The method assumes that the file path is root relative, and follows MVC conventions: /// /// * path with an Area: /// ~/Areas/TestArea/Views/home/index/indexspecific.css /// /// * path without an Area: /// ~/Views/home/index/indexspecific.css /// /// If the file path doesn't adhere to this, the method returns /// @ /// </summary> /// <param name="filePath"></param> /// <returns></returns> public static string FilePathSortKey(AssetPath assetPath) { string filePath = assetPath.RootRelativePath; if (!filePath.StartsWith("~/")) { throw new Exception(string.Format("FilePathSortKey - filePath {0} does not start with ~/", filePath)); } string[] filePathComponents = filePath.Split(new char[] { '/' }); // Note that the first element in filePathComponents will be the ~ // The last element is the file name itself. string area = "@"; int nbrComponents = filePathComponents.Length; int controllerIdx = 2; if (string.CompareOrdinal(filePathComponents[1], "Areas") == 0) { if ((nbrComponents < 6) || (string.CompareOrdinal(filePathComponents[3], "Views") != 0)) { return("@"); } area = filePathComponents[2]; controllerIdx = 4; } else if (string.CompareOrdinal(filePathComponents[1], "Views") == 0) { if (nbrComponents < 4) { return("@"); } } else { return("@"); } string controller = filePathComponents[controllerIdx]; if (string.CompareOrdinal(controller, "Shared") == 0) { controller = "@"; } string postfix = "@"; int postControllerDirectoryIdx = (controllerIdx + 1); if (nbrComponents > postControllerDirectoryIdx) { string postControllerDirectory = filePathComponents[postControllerDirectoryIdx].ToLower(); if (postControllerDirectory.StartsWith("_layout")) { postfix = ""; } } string sortKey = area + "___" + controller + postfix; return(sortKey); }