1+ // Code generation entry point for the MapLibre Flutter workspace.
2+ //
3+ // This script reads the canonical style definition (style.json) and produces
4+ // strongly-typed Dart (and some platform specific Java/Swift) sources that
5+ // expose layer & source properties plus expression helpers.
6+ //
7+ // Key goals:
8+ // - Keep hand‑written logic small; most surface area is generated.
9+ // - Ensure deterministic output (stable ordering helps minimal diffs).
10+ // - Apply formatting immediately so CI "format-all" never fails after generation.
11+ //
12+ // Notes:
13+ // - Do NOT edit the generated files manually; instead adjust templates or
14+ // this generator.
15+ // - The generator intentionally avoids adding a per‑file language version
16+ // pragma to prevent part <-> library mismatches (see earlier CI issue).
17+ // - If the style specification evolves, update templates & mapping tables
18+ // inside conversions.dart.
19+ // - Run via: melos run generate
20+ //
121import 'dart:io' ;
222import 'dart:convert' ;
323
@@ -9,10 +29,14 @@ import 'package:pub_semver/pub_semver.dart';
929import 'conversions.dart' ;
1030
1131Future <void > main () async {
32+ /// We assume the current working directory for this script is the scripts/
33+ /// package root (melos exec enforces that). style.json lives under input/.
1234 final currentPath = Directory .current.path;
1335 final styleFilePath = '$currentPath /input/style.json' ;
1436 final styleJson = jsonDecode (await File (styleFilePath).readAsString ());
1537
38+ /// Layer types in the order we want to render them. Order matters for
39+ /// deterministic output & smaller diffs.
1640 final layerTypes = [
1741 "symbol" ,
1842 "circle" ,
@@ -24,6 +48,8 @@ Future<void> main() async {
2448 "heatmap" ,
2549 ];
2650
51+ /// Source types. The template will convert snake_case to
52+ /// the appropriate casing for class names and enum-like strings.
2753 final sourceTypes = [
2854 "vector" ,
2955 "raster" ,
@@ -33,6 +59,9 @@ Future<void> main() async {
3359 "image"
3460 ];
3561
62+ /// Build the mustache rendering context consumed by each template.
63+ /// Most heavy lifting (doc splitting, type inference) happens in helper
64+ /// functions below for clarity & reuse.
3665 final renderContext = {
3766 "layerTypes" : [
3867 for (final type in layerTypes)
@@ -56,11 +85,15 @@ Future<void> main() async {
5685 };
5786
5887 // required for deduplication
88+ // Collect a set of all layout property names across layer types to enable
89+ // template logic for shared helpers / deduplication.
5990 renderContext["all_layout_properties" ] = < dynamic > {
6091 for (final type in renderContext["layerTypes" ]! )
6192 ...type["layout_properties" ].map ((p) => p["value" ])
6293 }.map ((p) => {"property" : p}).toList ();
6394
95+ // Ordered list of templates we render. If you add a new feature, append
96+ // here to keep existing diff noise minimal.
6497 const templates = [
6598 "maplibre_gl/android/src/main/java/org/maplibre/maplibregl/LayerPropertyConverter.java" ,
6699 "maplibre_gl/ios/maplibre_gl/Sources/maplibre_gl/LayerPropertyConverter.swift" ,
@@ -75,6 +108,9 @@ Future<void> main() async {
75108 }
76109}
77110
111+ /// Render a single template file.
112+ /// [path] is the relative workspace path to the output (and indirectly the
113+ /// template at scripts/templates/$filename.template).
78114Future <void > render (
79115 Map <String , List > renderContext,
80116 String path,
@@ -104,11 +140,6 @@ Future<void> render(
104140 final languageVersion =
105141 '${versionParts .elementAt (0 )}.${versionParts .elementAt (1 )}' ;
106142
107- // Prepend language version pragma if not already present
108- if (! rendered.startsWith ('// @dart=' )) {
109- rendered = '// @dart=$languageVersion \n\n $rendered ' ;
110- }
111-
112143 // Pass the version to the formatter so it can format accordingly
113144 final versionObj = Version .parse ('$languageVersion .0' );
114145 final formatter = DartFormatter (languageVersion: versionObj);
@@ -122,13 +153,15 @@ Future<void> render(
122153 await outputFile.writeAsString (rendered);
123154}
124155
156+ /// Build the (paint/layout) style properties list for a given style.json key.
125157List <Map <String , dynamic >> buildStyleProperties (
126158 Map <String , dynamic > styleJson, String key) {
127159 final Map <String , dynamic > items = styleJson[key];
128160
129161 return items.entries.map ((e) => buildStyleProperty (e.key, e.value)).toList ();
130162}
131163
164+ /// Translate a single raw style property spec into a template-ready map.
132165Map <String , dynamic > buildStyleProperty (
133166 String key, Map <String , dynamic > value) {
134167 final typeDart = dartTypeMappingTable[value["type" ]];
@@ -149,6 +182,7 @@ Map<String, dynamic> buildStyleProperty(
149182 };
150183}
151184
185+ /// Build the list of source properties (excluding generic wildcard entries).
152186List <Map <String , dynamic >> buildSourceProperties (
153187 Map <String , dynamic > styleJson, String key) {
154188 final Map <String , dynamic > items = styleJson[key];
@@ -159,6 +193,8 @@ List<Map<String, dynamic>> buildSourceProperties(
159193 .toList ();
160194}
161195
196+ /// Translate one source property spec to a template map, including default
197+ /// value normalization (prefixing const for literal lists, quoting strings).
162198Map <String , dynamic > buildSourceProperty (
163199 String key, Map <String , dynamic > value) {
164200 final camelCase = ReCase (key).camelCase;
@@ -189,6 +225,8 @@ Map<String, dynamic> buildSourceProperty(
189225 };
190226}
191227
228+ /// Produce a wrapped documentation block (array of lines) including
229+ /// type/default/constraints plus enumerated option docs.
192230List <String > buildDocSplit (Map <String , dynamic > item) {
193231 final defaultValue = item["default" ];
194232 final maxValue = item["maximum" ];
@@ -230,6 +268,7 @@ List<String> buildDocSplit(Map<String, dynamic> item) {
230268 return result;
231269}
232270
271+ /// Simple greedy word-wrapping utility used for docs.
233272List <String > splitIntoChunks (String input, int lineLength,
234273 {String prefix = "" }) {
235274 final words = input.split (" " );
@@ -250,6 +289,8 @@ List<String> splitIntoChunks(String input, int lineLength,
250289 return chunks;
251290}
252291
292+ /// Build expression metadata (renaming reserved or symbolic operators to
293+ /// valid method-like identifiers for Dart code generation).
253294List <Map <String , dynamic >> buildExpressionProperties (
254295 Map <String , dynamic > styleJson) {
255296 final Map <String , dynamic > items = styleJson["expression_name" ]["values" ];
0 commit comments