private void HangUntilFileChange(HttpListenerContext context) { SafeTask.Run(() => { // Log it CoreLogger.Log("Waiting for file change signal..."); // Wait here until files change mContentChangedResetEvent.WaitOne(); // If we are stopping, just return if (Stopping) { return; } // Log it CoreLogger.LogInformation("Refreshing web page request..."); // Response code OK (200) context.Response.StatusCode = (int)HttpStatusCode.OK; // End stream context.Response.OutputStream.Close(); }); }
/// <summary> /// Save the files contents to the destination location /// </summary> /// <param name="processingData"></param> /// <param name="outputPath"></param> /// <returns></returns> public override Task SaveFileContents(FileProcessingData processingData, FileOutputData outputPath) { return(SafeTask.Run(() => { // Do normal file copy when saving static file FileManager.CopyFile(processingData.FullPath, outputPath.FullPath); })); }
protected override Task PreProcessFile(FileProcessingData data) { return(SafeTask.Run(() => { // Set this file to partial if it starts with _ data.IsPartial = Path.GetFileName(data.FullPath).StartsWith("_"); })); }
/// <summary> /// Specifies Sass output paths based on Dna configuration settings, if specified /// </summary> /// <param name="data"></param> /// <returns></returns> protected override Task PostProcessOutputPaths(FileProcessingData data) { return(SafeTask.Run(() => { // For now just output in the same directory as Sass file data.OutputPaths.Add(new FileOutputData { FullPath = GetDefaultOutputPath(data), FileContents = data.UnprocessedFileContents }); })); }
/// <summary> /// Specifies output paths based on Dna configuration settings, if specified /// </summary> /// <param name="data"></param> /// <returns></returns> protected override Task PostProcessOutputPaths(FileProcessingData data) { return(SafeTask.Run(() => { // Output path is the destination folder var relativePath = data.FullPath.Substring(StaticFolderDetails.SourceFolder.Length + 1); var outputPath = Path.GetFullPath(Path.Combine(StaticFolderDetails.DestinationFolder, relativePath)); data.OutputPaths.Add(new FileOutputData { FullPath = outputPath, FileContents = data.UnprocessedFileContents }); })); }
protected override Task PreProcessFile(FileProcessingData data) { return(SafeTask.Run(() => { // We don't need any processing for static files // It is all done via the Static engine WillProcessDataTags = false; WillProcessMainTags = false; WillProcessOutputTags = false; WillProcessVariables = false; WillProcessLiveVariables = false; // Don't read the file into memory WillReadFileIntoMemory = false; })); }
/// <summary> /// Delete the same folder at the destination /// </summary> /// <param name="path">The source folder that has been deleted</param> /// <returns></returns> protected override Task ProcessFolderDeletedAsync(string path) { return(SafeTask.Run(() => { var relativePath = path.Substring(StaticFolderDetails.SourceFolder.Length + 1); var outputPath = Path.GetFullPath(Path.Combine(StaticFolderDetails.DestinationFolder, relativePath)); // Sanity check we are still inside output path if (!outputPath.Replace("/", "\\").StartsWith(StaticFolderDetails.DestinationFolder + "\\")) { CoreLogger.Log($"Ignoring folder delete as it appears outside of the destination folder. Source: '{path}' Destination: '{outputPath}'"); return; } // Log it CoreLogger.Log($"Deleting static folder {outputPath}", type: LogType.Warning); // Delete folder FileManager.DeleteFolder(outputPath); })); }
protected override Task PreProcessFile(FileProcessingData data) { return(SafeTask.Run(() => { // We don't need any processing for Sass files // It is all done via the Sass engine WillProcessDataTags = false; WillProcessMainTags = false; WillProcessOutputTags = false; WillProcessVariables = false; WillProcessLiveVariables = false; // Set this file to partial if it starts with _ // As per the Sass rule data.IsPartial = Path.GetFileName(data.FullPath).StartsWith("_"); // Ignore .sass-cache folder if (data.FullPath.Replace("\\", "/").Contains("/.sass-cache/")) { data.SkipMessage = "Ignoring .sass-cache folder"; } })); }
/// <summary> /// Starts listening on the specified <see cref="Port"/> /// </summary> /// <returns>Returns the URL that is being listened on</returns> public string Listen() { lock (mListenLock) { // If we are already listening... if (Listening) { // Ignore return(null); } #region Get Port try { // Get port if one is not specified if (Port <= 0) { // Log it CoreLogger.Log("LiveServer getting available port..."); // Get next available port Port = NextAvailablePort(); } } catch (Exception ex) { // Log it Debugger.Break(); CoreLogger.Log($"LiveServer failed to find an available port. {ex.Message}", type: LogType.Error); // Go no further return(null); } // Log port to be used CoreLogger.Log($"LiveServer will listen on port {Port}"); #endregion // Expected listen URL var listenUrl = $"http://localhost:{Port}/"; #region Listen try { // Create new Http Listener mListener = new HttpListener(); // Set port number mListener.Prefixes.Add(listenUrl); // Start listening mListener.Start(); // Run new thread listening for content // until we call Stop or Dispose SafeTask.Run(() => ListenForContent()); // Set Listening flag Listening = true; // Log it CoreLogger.Log($"LiveServer listening on http://localhost:{Port}, directory '{ServingDirectory}'", type: LogType.Information); } catch (Exception ex) { // Log it Debugger.Break(); CoreLogger.Log($"LiveServer failed to start on port {Port}, directory '{ServingDirectory}'. {ex.Message}", type: LogType.Error); // Go no further return(null); } #endregion #region File Change Watcher mFolderWatcher = new FolderWatcher { Filter = "*", Path = ServingDirectory, UpdateDelay = 100 }; // Listen for file changes mFolderWatcher.FileChanged += FolderWatcher_FileChanged; mFolderWatcher.Start(); #endregion // Return URL we are now listening on return(listenUrl); } }
/// <summary> /// Replace C# specific tags /// </summary> /// <param name="data">The file data</param> /// <returns></returns> protected override Task PostProcessFile(FileProcessingData data) { return(SafeTask.Run(() => { // For each output data.OutputPaths.ForEach(output => { // Find all C# data sets Match match = null; // No error to start with data.Error = string.Empty; // Loop through all matches while (match == null || match.Success) { // Find the next data region match = Regex.Match(output.FileContents, mCSharp2GroupRegex, RegexOptions.Singleline); if (!match.Success) { return; } // NOTE: The first group is the full match // The second group is the type // Third group is data // Make sure we have enough groups if (match.Groups.Count < 3) { data.Error = $"Malformed match {match.Value}"; return; } // Take the first match as the header for the type of tag var type = match.Groups[1].Value.Trim(); var datasString = match.Groups[2].Value.Trim(); // Split the data via ; and trim it // Example data would be: group=DomIds; value=Something with space; List <CSharpTagDataItem> propertyData = null; try { propertyData = datasString.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(f => { // Get data string var dataString = f?.Trim(); // Make sure we have at least one = after a character if (dataString.IndexOf('=') <= 0) { throw new ArgumentException("Invalid CSharp data item: " + dataString); } // Get name and value var name = dataString.Substring(0, dataString.IndexOf('=')); var value = dataString.Substring(dataString.IndexOf('=') + 1); return new CSharpTagDataItem { Name = name, Value = value }; }).ToList(); } catch (Exception ex) { data.Error = ex.Message; return; } switch (type) { case "properties": // Process the property data ProcessPropertiesTab(data, output, match, propertyData); // Check for success if (!data.Successful) { // Return if it fails return; } break; default: // Report error of unknown match data.Error = $"Unknown match {match.Value}"; return; } } }); })); }