Skip to content

Commit dc1f553

Browse files
falahatimadskristensen
authored andcommitted
Handlebars Compiler Added (#291)
* Initial Support for Handlebars added * Increasing Package Version to force redownload node modules * Adding node directory to .gitignore file * Updating extension manifest file * Source Map file creation support for Handlebars compiler added * Basic Tests for Handlebars added * Temporally map file name changed to prevent unintended overwrites * Handlebars: Spelling mistake fixed
1 parent 313198e commit dc1f553

27 files changed

+626
-31
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ x64/
2222
# NCrunch
2323
*.ncrunchsolution
2424
*.ncrunchproject
25-
_NCrunch_WebCompiler
25+
_NCrunch_WebCompiler#Node
26+
27+
#Node
28+
src/WebCompiler/Node/node_modules/*

build/build.cmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ call npm install --quiet ^
1717
less-plugin-csscomb ^
1818
node-sass ^
1919
stylus ^
20+
handlebars ^
2021
> nul
2122

2223
if not exist "node_modules\node-sass\vendor\win32-ia32-14" (

src/WebCompiler/Compile/CompilerService.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ namespace WebCompiler
1010
/// </summary>
1111
public static class CompilerService
1212
{
13-
internal const string Version = "1.4.166";
13+
internal const string Version = "1.4.167";
1414
private static readonly string _path = Path.Combine(Path.GetTempPath(), "WebCompiler" + Version);
1515
private static object _syncRoot = new object(); // Used to lock on the initialize step
1616

1717
/// <summary>A list of allowed file extensions.</summary>
18-
public static readonly string[] AllowedExtensions = new[] { ".LESS", ".SCSS", ".SASS", ".STYL", ".COFFEE", ".ICED", ".JS", ".JSX", ".ES6" };
18+
public static readonly string[] AllowedExtensions = new[] { ".LESS", ".SCSS", ".SASS", ".STYL", ".COFFEE", ".ICED", ".JS", ".JSX", ".ES6", ".HBS", ".HANDLEBARS" };
1919

2020
/// <summary>
2121
/// Test if a file type is supported by the compilers.
@@ -42,6 +42,11 @@ internal static ICompiler GetCompiler(Config config)
4242
compiler = new LessCompiler(_path);
4343
break;
4444

45+
case ".HANDLEBARS":
46+
case ".HBS":
47+
compiler = new HandlebarsCompiler(_path);
48+
break;
49+
4550
case ".SCSS":
4651
case ".SASS":
4752
compiler = new SassCompiler(_path);
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Text;
5+
using System.Text.RegularExpressions;
6+
7+
namespace WebCompiler
8+
{
9+
class HandlebarsCompiler : ICompiler
10+
{
11+
private static Regex _errorRx = new Regex("Error: (?<message>.+) on line (?<line>[0-9]+):", RegexOptions.Compiled);
12+
private string _mapPath;
13+
private string _path;
14+
private string _name = string.Empty;
15+
private string _extension = string.Empty;
16+
private string _output = string.Empty;
17+
private string _error = string.Empty;
18+
private bool _partial = false;
19+
20+
public HandlebarsCompiler(string path)
21+
{
22+
_path = path;
23+
}
24+
25+
public CompilerResult Compile(Config config)
26+
{
27+
string baseFolder = Path.GetDirectoryName(config.FileName);
28+
string inputFile = Path.Combine(baseFolder, config.InputFile);
29+
30+
FileInfo info = new FileInfo(inputFile);
31+
string content = File.ReadAllText(info.FullName);
32+
33+
CompilerResult result = new CompilerResult
34+
{
35+
FileName = info.FullName,
36+
OriginalContent = content,
37+
};
38+
39+
var extension = Path.GetExtension(inputFile);
40+
if (!string.IsNullOrWhiteSpace(extension))
41+
{
42+
_extension = extension.Substring(1);
43+
}
44+
45+
var name = Path.GetFileNameWithoutExtension(inputFile);
46+
if (!string.IsNullOrWhiteSpace(name) && name.StartsWith("_"))
47+
{
48+
_name = name.Substring(1);
49+
_partial = true;
50+
51+
// Temporarily Fix
52+
// TODO: Remove after actual fix
53+
var tempFilename = Path.Combine(Path.GetDirectoryName(inputFile), _name + ".handlebarstemp");
54+
info.CopyTo(tempFilename);
55+
info = new FileInfo(tempFilename);
56+
_extension = "handlebarstemp";
57+
}
58+
59+
_mapPath = Path.ChangeExtension(inputFile, ".js.map.tmp");
60+
61+
try
62+
{
63+
RunCompilerProcess(config, info);
64+
65+
result.CompiledContent = _output;
66+
67+
var options = HandlebarsOptions.FromConfig(config);
68+
if (options.SourceMap || config.SourceMap)
69+
{
70+
if (File.Exists(_mapPath))
71+
result.SourceMap = File.ReadAllText(_mapPath);
72+
}
73+
74+
if (_error.Length > 0)
75+
{
76+
CompilerError ce = new CompilerError
77+
{
78+
FileName = inputFile,
79+
Message = _error.Replace(baseFolder, string.Empty),
80+
IsWarning = !string.IsNullOrEmpty(_output)
81+
};
82+
83+
var match = _errorRx.Match(_error);
84+
85+
if (match.Success)
86+
{
87+
ce.Message = match.Groups["message"].Value.Replace(baseFolder, string.Empty);
88+
ce.LineNumber = int.Parse(match.Groups["line"].Value);
89+
ce.ColumnNumber = 0;
90+
}
91+
92+
result.Errors.Add(ce);
93+
}
94+
}
95+
catch (Exception ex)
96+
{
97+
CompilerError error = new CompilerError
98+
{
99+
FileName = inputFile,
100+
Message = string.IsNullOrEmpty(_error) ? ex.Message : _error,
101+
LineNumber = 0,
102+
ColumnNumber = 0,
103+
};
104+
105+
result.Errors.Add(error);
106+
}
107+
finally
108+
{
109+
if (File.Exists(_mapPath))
110+
{
111+
File.Delete(_mapPath);
112+
}
113+
// Temporarily Fix
114+
// TODO: Remove after actual fix
115+
if (info.Extension == ".handlebarstemp")
116+
{
117+
info.Delete();
118+
}
119+
}
120+
121+
return result;
122+
}
123+
124+
private void RunCompilerProcess(Config config, FileInfo info)
125+
{
126+
string arguments = ConstructArguments(config);
127+
128+
ProcessStartInfo start = new ProcessStartInfo
129+
{
130+
WorkingDirectory = info.Directory.FullName,
131+
UseShellExecute = false,
132+
WindowStyle = ProcessWindowStyle.Hidden,
133+
CreateNoWindow = true,
134+
FileName = "cmd.exe",
135+
Arguments = $"/c \"\"{Path.Combine(_path, "node_modules\\.bin\\handlebars.cmd")}\" \"{info.FullName}\" {arguments}\"",
136+
StandardOutputEncoding = Encoding.UTF8,
137+
StandardErrorEncoding = Encoding.UTF8,
138+
RedirectStandardOutput = true,
139+
RedirectStandardError = true,
140+
};
141+
142+
start.EnvironmentVariables["PATH"] = _path + ";" + start.EnvironmentVariables["PATH"];
143+
144+
using (Process p = Process.Start(start))
145+
{
146+
var stdout = p.StandardOutput.ReadToEndAsync();
147+
var stderr = p.StandardError.ReadToEndAsync();
148+
p.WaitForExit();
149+
150+
_output = stdout.Result.Trim();
151+
_error = stderr.Result.Trim();
152+
}
153+
}
154+
155+
private string ConstructArguments(Config config)
156+
{
157+
string arguments = "";
158+
159+
HandlebarsOptions options = HandlebarsOptions.FromConfig(config);
160+
161+
if (options.AMD)
162+
arguments += " --amd";
163+
else if (!string.IsNullOrEmpty(options.CommonJS))
164+
arguments += $" --commonjs \"{options.CommonJS}\"";
165+
166+
foreach (var knownHelper in options.KnownHelpers)
167+
{
168+
arguments += $" --known \"{knownHelper}\"";
169+
}
170+
171+
if (options.KnownHelpersOnly)
172+
arguments += " --knownOnly";
173+
174+
if (options.ForcePartial || _partial)
175+
arguments += " --partial";
176+
177+
if (options.NoBOM)
178+
arguments += " --bom";
179+
180+
if ((options.SourceMap || config.SourceMap) && !string.IsNullOrWhiteSpace(_mapPath))
181+
arguments += $" --map \"{_mapPath}\"";
182+
183+
if (!string.IsNullOrEmpty(options.TemplateNameSpace))
184+
arguments += $" --namespace \"{options.TemplateNameSpace}\"";
185+
186+
if (!string.IsNullOrEmpty(options.Root))
187+
arguments += $" --root \"{options.Root}\"";
188+
189+
if (!string.IsNullOrEmpty(options.Name))
190+
arguments += $" --name \"{options.Name}\"";
191+
else if (!string.IsNullOrEmpty(_name))
192+
arguments += $" --name \"{_name}\"";
193+
194+
if (!string.IsNullOrEmpty(_extension))
195+
arguments += $" --extension \"{_extension}\"";
196+
197+
return arguments;
198+
}
199+
}
200+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using Newtonsoft.Json;
2+
using System.Linq;
3+
4+
namespace WebCompiler
5+
{
6+
/// <summary>
7+
/// Give all options for the Handlebars compiler
8+
/// </summary>
9+
public class HandlebarsOptions : BaseOptions<HandlebarsOptions>
10+
{
11+
private const string trueStr = "true";
12+
13+
/// <summary> Creates a new instance of the class.</summary>
14+
public HandlebarsOptions()
15+
{ }
16+
17+
/// <summary>
18+
/// Load the settings from the config object
19+
/// </summary>
20+
protected override void LoadSettings(Config config)
21+
{
22+
base.LoadSettings(config);
23+
24+
var name = GetValue(config, "name");
25+
if (name != null)
26+
Name = name;
27+
28+
var @namespace = GetValue(config, "namespace");
29+
if (@namespace != null)
30+
TemplateNameSpace = @namespace;
31+
32+
var root = GetValue(config, "root");
33+
if (root != null)
34+
Root = root;
35+
36+
var commonjs = GetValue(config, "commonjs");
37+
if (commonjs != null)
38+
CommonJS = commonjs;
39+
40+
var amd = GetValue(config, "amd");
41+
if (amd != null)
42+
AMD = amd.ToLowerInvariant() == trueStr;
43+
44+
var forcePartial = GetValue(config, "forcePartial");
45+
if (forcePartial != null)
46+
ForcePartial = forcePartial.ToLowerInvariant() == trueStr;
47+
48+
var noBOM = GetValue(config, "noBOM");
49+
if (noBOM != null)
50+
NoBOM = noBOM.ToLowerInvariant() == trueStr;
51+
52+
var knownHelpersOnly = GetValue(config, "knownHelpersOnly");
53+
if (knownHelpersOnly != null)
54+
KnownHelpersOnly = knownHelpersOnly.ToLowerInvariant() == trueStr;
55+
56+
var knownHelpers = GetValue(config, "knownHelpers");
57+
if (knownHelpers != null)
58+
KnownHelpers = knownHelpers.Split(',').Where(s => !string.IsNullOrWhiteSpace(s)).Select(s => s.Trim()).ToArray();
59+
}
60+
61+
/// <summary>
62+
/// The file name should match the compiler name
63+
/// </summary>
64+
protected override string CompilerFileName
65+
{
66+
get { return "hbs"; }
67+
}
68+
69+
/// <summary>
70+
/// Template root. Base value that will be stripped from template names.
71+
/// </summary>
72+
[JsonProperty("root")]
73+
public string Root { get; set; } = "";
74+
75+
/// <summary>
76+
/// Removes the BOM (Byte Order Mark) from the beginning of the templates.
77+
/// </summary>
78+
[JsonProperty("noBOM")]
79+
public bool NoBOM { get; set; } = false;
80+
81+
/// <summary>
82+
/// Name of passed string templates.
83+
/// </summary>
84+
[JsonProperty("name")]
85+
public string Name { get; set; } = "";
86+
87+
/// <summary>
88+
/// Template namespace
89+
/// </summary>
90+
[JsonProperty("namespace")]
91+
public string TemplateNameSpace { get; set; } = "";
92+
93+
/// <summary>
94+
/// Compile with known helpers only
95+
/// </summary>
96+
[JsonProperty("knownHelpersOnly")]
97+
public bool KnownHelpersOnly { get; set; } = false;
98+
99+
100+
/// <summary>
101+
/// Forcing a partial template compilation
102+
/// </summary>
103+
[JsonProperty("forcePartial")]
104+
public bool ForcePartial { get; set; } = false;
105+
106+
/// <summary>
107+
/// List of known helpers for a more optimized output
108+
/// </summary>
109+
[JsonProperty("knownHelpers")]
110+
public string[] KnownHelpers { get; set; } = new string[0];
111+
112+
/// <summary>
113+
/// Path to the Handlebars module to export CommonJS style
114+
/// </summary>
115+
[JsonProperty("commonjs")]
116+
public string CommonJS { get; set; } = "";
117+
118+
/// <summary>
119+
/// Exports amd style (require.js), this option has priority to commonjs.
120+
/// </summary>
121+
[JsonProperty("amd")]
122+
public bool AMD { get; set; } = false;
123+
}
124+
}

src/WebCompiler/Config/ConfigHandler.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public void CreateDefaultsFile(string fileName)
6767
stylus = new StylusOptions(),
6868
babel = new BabelOptions(),
6969
coffeescript = new IcedCoffeeScriptOptions(),
70+
handlebars = new HandlebarsOptions(),
7071
},
7172
minifiers = new
7273
{

src/WebCompiler/Dependencies/DependencyService.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ private static DependencyType GetDependencyType(string sourceFile)
7979
case ".ICED":
8080
return DependencyType.None;
8181

82+
case ".HBS":
83+
case ".HANDLEBARS":
84+
return DependencyType.None;
85+
8286
case ".JS":
8387
case ".JSX":
8488
case ".ES6":

src/WebCompiler/WebCompiler.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
<Compile Include="Compile\IcedCoffeeScriptCompiler.cs" />
6868
<Compile Include="Compile\CompilerService.cs" />
6969
<Compile Include="Compile\ICompiler.cs" />
70+
<Compile Include="Compile\HandlebarsCompiler.cs" />
71+
<Compile Include="Compile\HandlebarsOptions.cs" />
7072
<Compile Include="Compile\StylusCompiler.cs" />
7173
<Compile Include="Compile\BabelOptions.cs" />
7274
<Compile Include="Compile\StylusOptions.cs" />

0 commit comments

Comments
 (0)