Skip to content

Commit 3f52791

Browse files
committed
Add liquid processor configuration file option to support custom inline and block tags
1 parent 7c20776 commit 3f52791

5 files changed

Lines changed: 160 additions & 6 deletions

File tree

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ dotnet tool install --global OrchardCoreContrib.PoExtractor
1515
## Usage
1616

1717
```powershell
18-
extractpo <INTPUT_PATH> <OUTPUT_PATH> [-l|--language {"C#"|"VB"}] [-t|--template {"razor"|"liquid"}]
18+
extractpo <INTPUT_PATH> <OUTPUT_PATH> [-l|--language {"C#"|"VB"}] [-t|--template {"razor"|"liquid"}] [--liquid-processor-configuration {path to JSON file}]
1919
```
2020

2121
### Description
@@ -60,6 +60,19 @@ When executing the plugins, all _OrchardCoreContrib.PoExtractor_ assemblies are
6060
> Console.WriteLine("Imported resource name: {0}", ResourceNames.ShoppingCart);
6161
> ```
6262
63+
- **`--liquid-processor-configuration {path to JSON file}`**
64+
65+
Specifies the path to a JSON file with `LiquidProcessorConfiguration`, containing `InlineTags` and `BlockTags` arrays used to register custom Liquid tags during parsing.
66+
67+
Example:
68+
69+
```json
70+
{
71+
"InlineTags": ["resources", "link", "script", "style"],
72+
"BlockTags": ["edit_custom_settings", "scriptblock", "styleblock", "EditCustomSettings"]
73+
}
74+
```
75+
6376
## Uninstallation
6477

6578
```powershell
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace OrchardCoreContrib.PoExtractor.Liquid;
2+
3+
public class LiquidProcessorConfiguration
4+
{
5+
public IReadOnlyList<string> InlineTags { get; set; } = [];
6+
7+
public IReadOnlyList<string> BlockTags { get; set; } = [];
8+
}

src/OrchardCoreContrib.PoExtractor.Liquid/LiquidProjectProcessor.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,40 @@ public class LiquidProjectProcessor : IProjectProcessor
1818
/// <summary>
1919
/// Initializes a new instance of the <see cref="LiquidProjectProcessor"/>
2020
/// </summary>
21-
public LiquidProjectProcessor()
21+
public LiquidProjectProcessor(LiquidProcessorConfiguration configuration = null)
2222
{
23-
var liquidViewOptions = Options.Create(new LiquidViewOptions());
23+
configuration ??= new LiquidProcessorConfiguration();
24+
25+
var liquidViewOptions = new LiquidViewOptions();
26+
liquidViewOptions.LiquidViewParserConfiguration.Add(parser =>
27+
{
28+
foreach (var tag in NormalizeTags(configuration.InlineTags))
29+
{
30+
parser.RegisterParserTag(
31+
tag,
32+
parser.ArgumentsListParser,
33+
static (_, _, _, _) => System.Threading.Tasks.ValueTask.FromResult(Fluid.Ast.Completion.Normal));
34+
}
35+
36+
foreach (var tag in NormalizeTags(configuration.BlockTags))
37+
{
38+
parser.RegisterParserBlock(
39+
tag,
40+
parser.ArgumentsListParser,
41+
static (_, _, _, _, _) => System.Threading.Tasks.ValueTask.FromResult(Fluid.Ast.Completion.Normal));
42+
}
43+
});
44+
2445
var fileParserOptions = Options.Create(new FluidParserOptions());
2546

26-
_parser = new LiquidViewParser(liquidViewOptions, fileParserOptions);
47+
_parser = new LiquidViewParser(Options.Create(liquidViewOptions), fileParserOptions);
2748
}
2849

50+
private static IEnumerable<string> NormalizeTags(IReadOnlyList<string> tags) => tags?
51+
.Where(static tag => !string.IsNullOrWhiteSpace(tag))
52+
.Distinct(StringComparer.Ordinal)
53+
?? [];
54+
2955
/// <inheritdoc/>
3056
public void Process(string path, string basePath, LocalizableStringCollection localizableStrings)
3157
{
@@ -36,6 +62,8 @@ public void Process(string path, string basePath, LocalizableStringCollection lo
3662
var liquidMetadataProvider = new LiquidMetadataProvider(basePath);
3763
var liquidVisitor = new ExtractingLiquidWalker([new LiquidStringExtractor(liquidMetadataProvider)], localizableStrings);
3864

65+
List<string> allErrors = new List<string>();
66+
3967
foreach (var file in Directory.EnumerateFiles(path, $"*{_liquidExtension}", SearchOption.AllDirectories).OrderBy(file => file))
4068
{
4169
using var stream = File.OpenRead(file);
@@ -44,6 +72,10 @@ public void Process(string path, string basePath, LocalizableStringCollection lo
4472
{
4573
ProcessTemplate(template, liquidVisitor, file);
4674
}
75+
else
76+
{
77+
Console.WriteLine($"Error: {errors}, file: {file}");
78+
}
4779
}
4880
}
4981

src/OrchardCoreContrib.PoExtractor/Program.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.ComponentModel.DataAnnotations;
2+
using System.Text.Json;
23
using McMaster.Extensions.CommandLineUtils;
34
using OrchardCore.Modules;
45
using OrchardCoreContrib.PoExtractor.DotNet;
@@ -37,6 +38,33 @@ public static void Main(string[] args)
3738
var ignoredProjects = app.Option("-i|--ignore <IGNORED_PROJECTS>", "Ignores extracting PO files from a given project(s).", CommandOptionType.MultipleValue);
3839
var localizers = app.Option("--localizer <LOCALIZERS>", "Specifies the name of the localizer(s) that will be used during the extraction process.", CommandOptionType.MultipleValue);
3940
var single = app.Option("-s|--single <FILE_NAME>", "Specifies the single output file.", CommandOptionType.SingleValue);
41+
var liquidProcessorConfigurationPath = app.Option(
42+
"--liquid-processor-configuration <FILE_NAME>",
43+
"Specifies a path to a JSON file with LiquidProcessorConfiguration (InlineTags and BlockTags).",
44+
CommandOptionType.SingleValue,
45+
option => option.OnValidate(_ =>
46+
{
47+
if (!option.HasValue())
48+
{
49+
return ValidationResult.Success;
50+
}
51+
52+
if (!File.Exists(option.Value()))
53+
{
54+
return new ValidationResult("Liquid processor configuration must be an existing local file.");
55+
}
56+
57+
try
58+
{
59+
LoadLiquidProcessorConfiguration(option.Value());
60+
61+
return ValidationResult.Success;
62+
}
63+
catch (JsonException ex)
64+
{
65+
return new ValidationResult($"Liquid processor configuration must be valid JSON: {ex.Message}");
66+
}
67+
}));
4068
var plugins = app.Option(
4169
"-p|--plugin <FILE_NAME_OR_HTTPS_URL>",
4270
"A path or web URL with HTTPS scheme to a C# script (.csx) file which can define further " +
@@ -69,6 +97,9 @@ public static void Main(string[] args)
6997

7098
var projectFiles = new List<string>();
7199
var projectProcessors = new List<IProjectProcessor>();
100+
var liquidProcessorConfiguration = liquidProcessorConfigurationPath.HasValue()
101+
? LoadLiquidProcessorConfiguration(liquidProcessorConfigurationPath.Value())
102+
: new LiquidProcessorConfiguration();
72103

73104
if (language.Value() == Language.CSharp)
74105
{
@@ -90,15 +121,15 @@ public static void Main(string[] args)
90121
if (template.Value() == TemplateEngine.Both)
91122
{
92123
projectProcessors.Add(new RazorProjectProcessor());
93-
projectProcessors.Add(new LiquidProjectProcessor());
124+
projectProcessors.Add(new LiquidProjectProcessor(liquidProcessorConfiguration));
94125
}
95126
else if (template.Value() == TemplateEngine.Razor)
96127
{
97128
projectProcessors.Add(new RazorProjectProcessor());
98129
}
99130
else if (template.Value() == TemplateEngine.Liquid)
100131
{
101-
projectProcessors.Add(new LiquidProjectProcessor());
132+
projectProcessors.Add(new LiquidProjectProcessor(liquidProcessorConfiguration));
102133
}
103134

104135
if (plugins.Values.Count > 0)
@@ -161,4 +192,10 @@ public static void Main(string[] args)
161192

162193
app.Execute(args);
163194
}
195+
196+
private static LiquidProcessorConfiguration LoadLiquidProcessorConfiguration(string path)
197+
=> JsonSerializer.Deserialize<LiquidProcessorConfiguration>(
198+
File.ReadAllText(path),
199+
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })
200+
?? new LiquidProcessorConfiguration();
164201
}

test/OrchardCoreContrib.PoExtractor.Tests/ProgramTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,68 @@ public void Main_NoTemplateOption_UsesBothRazorAndLiquid()
6060
}
6161
}
6262
}
63+
64+
[Fact]
65+
public void Main_LiquidProcessorConfigurationOption_LoadsLiquidTagsFromJsonFile()
66+
{
67+
// Arrange
68+
var root = Path.Combine(Path.GetTempPath(), "PoExtractorTests", Guid.NewGuid().ToString("N"));
69+
var input = Path.Combine(root, "input");
70+
var output = Path.Combine(root, "output");
71+
var project = Path.Combine(input, "TestModule");
72+
var configurationPath = Path.Combine(root, "liquid-config.json");
73+
74+
Directory.CreateDirectory(project);
75+
Directory.CreateDirectory(output);
76+
77+
File.WriteAllText(Path.Combine(project, "TestModule.csproj"), """
78+
<Project Sdk="Microsoft.NET.Sdk">
79+
<PropertyGroup>
80+
<TargetFramework>net10.0</TargetFramework>
81+
</PropertyGroup>
82+
</Project>
83+
""");
84+
85+
File.WriteAllText(Path.Combine(project, "index.liquid"), """
86+
{% scriptblock at:"FootScript" %}
87+
{{ "Hello from Liquid configured tag" | t }}
88+
{% endscriptblock %}
89+
""");
90+
91+
File.WriteAllText(configurationPath, """
92+
{
93+
"InlineTags": [],
94+
"BlockTags": ["scriptblock"]
95+
}
96+
""");
97+
98+
var potFileName = "liquid-config.pot";
99+
100+
try
101+
{
102+
// Act
103+
Program.Main(
104+
[
105+
input,
106+
output,
107+
"--template", "Liquid",
108+
"--liquid-processor-configuration", configurationPath,
109+
"--single", potFileName
110+
]);
111+
112+
// Assert
113+
var potPath = Path.Combine(output, potFileName);
114+
Assert.True(File.Exists(potPath));
115+
116+
var pot = File.ReadAllText(potPath);
117+
Assert.Contains("Hello from Liquid configured tag", pot);
118+
}
119+
finally
120+
{
121+
if (Directory.Exists(root))
122+
{
123+
Directory.Delete(root, recursive: true);
124+
}
125+
}
126+
}
63127
}

0 commit comments

Comments
 (0)