1
1
import 'dart:async' ;
2
+ import 'dart:convert' ;
2
3
import 'dart:io' ;
4
+ import 'package:collection/collection.dart' ;
3
5
import 'package:configurator/configurator.dart' ;
4
6
import 'package:configurator/src/models/processed_config.dart' ;
5
7
import 'package:configurator/src/utils/string_ext.dart' ;
6
8
import 'package:dart_style/dart_style.dart' ;
7
9
import 'package:slang/builder/utils/path_utils.dart' ;
10
+ import 'package:yaml/yaml.dart' ;
11
+ import 'package:yaml_edit/yaml_edit.dart' ;
8
12
import 'config_file.dart' ;
9
13
import 'file_utils.dart' ;
10
14
import 'graph.dart' ;
15
+ import 'package:source_span/source_span.dart' ;
11
16
12
17
/// To run this:
13
18
/// -> flutter pub run configurator
14
- Future <void > main (List <String > arguments) async {
15
- final bool watch = arguments.contains ( '-w' ) || arguments.contains ( '--watch' );
16
-
17
- List <String > filters = arguments.join ('///' ).contains ('--id-filter=' )
18
- ? arguments.where ((a) => a.startsWith ( '--id-filter=' )).first.split ( '=' ).last.split ( ',' )
19
+ Future <void > main (List <String > args) async {
20
+ final bool applyDefs = args.contains ('--apply-defs' ) && args.contains ('-i' );
21
+
22
+ final bool watch = args.contains ('-w' ) || args.contains ('--watch' );
23
+
24
+ List <String > filters = args.join ('///' ).contains ('--id-filter=' )
25
+ ? args
26
+ .where ((a) => a.startsWith ('--id-filter=' ))
27
+ .first
28
+ .split ('=' )
29
+ .last
30
+ .split (',' )
19
31
: [];
20
32
21
- print ( '\n *****Configurator Starting!*****' );
33
+ print ('\n *****Configurator Starting!*****' );
22
34
23
- final stopwatch = Stopwatch ( );
35
+ List < FileSystemEntity > files = findConfigurations (filters );
24
36
25
- if (! watch) {
26
- stopwatch.start ();
37
+ if (applyDefs) {
38
+ var idx = args.indexOf ('-i' );
39
+ var input = args[idx + 1 ];
40
+
41
+ if (input.isEmpty) {
42
+ throw Exception ('Input not specified for definitions' );
43
+ }
44
+
45
+ if (! input.contains ('.defs.yaml' )) {
46
+ throw Exception ('Invalid file extension for definitions' );
47
+ }
48
+
49
+ await applyDefinitions (
50
+ files: files,
51
+ inputFile: input,
52
+ );
27
53
}
28
54
29
- List <FileSystemEntity > files = FileUtils .getFilesBreadthFirst (
55
+ configure (
56
+ files: files,
57
+ watch: watch,
58
+ );
59
+ }
60
+
61
+ List <FileSystemEntity > findConfigurations (List <String > filters) {
62
+ return FileUtils .getFilesBreadthFirst (
30
63
rootDirectory: Directory .current,
31
64
ignoreTopLevelDirectories: {
32
65
'.fvm' ,
@@ -40,15 +73,105 @@ Future<void> main(List<String> arguments) async {
40
73
'web' ,
41
74
},
42
75
).where ((file) {
43
-
44
- bool isConfig = file.path. endsWith ( '.config.yaml' );
45
- bool matchesFilter = filters.isEmpty || filters.contains ( file.path.getFileNameNoExtension () );
76
+ bool isConfig = file.path. endsWith ( '.config.yaml' );
77
+ bool matchesFilter =
78
+ filters.isEmpty || filters.contains (file.path.getFileNameNoExtension ());
46
79
47
80
return isConfig && matchesFilter;
48
81
}).toList ();
82
+ }
83
+
84
+ Future <void > applyDefinitions ({
85
+ required List <FileSystemEntity > files,
86
+ required String inputFile,
87
+ }) async {
88
+ final List <File > configFiles = files.map ((e) => File (e.path)).toList ();
89
+
90
+ final File definitionsFile = File (inputFile);
91
+
92
+ final YamlDocument document =
93
+ loadYamlDocument (definitionsFile.readAsStringSync ());
94
+ final YamlNode rootNode = document.contents;
95
+
96
+ final YamlNode definitions = rootNode.value['definitions' ];
97
+ final YamlMap colors = definitions.value['colors' ];
98
+
99
+ final Map colorsMap = Map .fromEntries (colors.entries);
100
+
101
+ for (var file in configFiles) {
102
+ var contents = file.readAsStringSync ();
103
+
104
+ List <String > lines = contents.split ('\n ' );
105
+
106
+ var colorNodeIdx = lines.indexWhere ((l) => l.contains ('colors:' ));
107
+
108
+ if (colorNodeIdx < 0 ) {
109
+ continue ;
110
+ }
111
+
112
+ var colorStartIdx = colorNodeIdx + 1 ;
113
+ var colorEndIdx = colorStartIdx;
114
+
115
+ var searchIdx = colorStartIdx + 1 ;
116
+
117
+ while (lines[searchIdx].startsWith (' ' )) {
118
+ colorEndIdx++ ;
119
+ searchIdx++ ;
120
+ }
121
+
122
+ List <String > newDefs = [];
123
+
124
+ for (var entry in colorsMap.entries) {
125
+ var line = lines.sublist (colorStartIdx, colorEndIdx + 1 ).firstWhereOrNull ((l) => l.trim ().startsWith ('${entry .key }:' ));
49
126
50
- print ( '\n ---Parsing Configs---' );
51
- print ( files.map ((e) => e.path).join ('\n ' ) );
127
+ if (line == null ) {
128
+ newDefs.add (' ${entry .key }: &${entry .key } "${entry .value }"' );
129
+ } else {
130
+ var lineIdx = lines.indexOf (line);
131
+
132
+ if (lineIdx > - 1 ) {
133
+ var oldLine = lines[lineIdx];
134
+ var newLine = ' ${entry .key }: &${entry .key } "${entry .value }"' ;
135
+
136
+ if (oldLine != newLine) {
137
+ lines[lineIdx] = ' ${entry .key }: &${entry .key } "${entry .value }"' ;
138
+ print ('Update Color Def -> $line -> ${lines [lineIdx ]}' );
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ if (newDefs.isNotEmpty) {
145
+ for (var def in newDefs) {
146
+ lines.insert (colorEndIdx, def);
147
+ colorEndIdx++ ;
148
+ print ('Insert Color Def -> $def ' );
149
+ }
150
+ }
151
+
152
+ var newDoc = lines.join ('\n ' );
153
+
154
+ try {
155
+ var parsed = YamlEditor (newDoc).toString ();
156
+ file.writeAsStringSync (parsed, flush: true );
157
+ } catch (e) {
158
+ file.writeAsStringSync (newDoc, flush: true );
159
+ }
160
+ }
161
+ }
162
+
163
+ Future <void > configure ({
164
+ required List <FileSystemEntity > files,
165
+ bool watch = false ,
166
+ }) async {
167
+ final stopwatch = Stopwatch ();
168
+
169
+ if (! watch) {
170
+ stopwatch.start ();
171
+ }
172
+
173
+ print ('\n ---Parsing Configs---' );
174
+ print (files.map ((e) => e.path).join ('\n ' ));
52
175
53
176
if (watch) {
54
177
await watchConfiguration (
@@ -71,107 +194,104 @@ Future<void> generateConfigurations({
71
194
final List <String > yamlInput = files.map ((e) => e.path).toList ();
72
195
73
196
// Ingest yaml as File(s)
74
- final List <File > yamlFiles = yamlInput.map ((e) => File ( e )).toList ();
197
+ final List <File > yamlFiles = yamlInput.map ((e) => File (e )).toList ();
75
198
76
199
// Convert to ConfigFiles (dir & config)
77
200
List <ConfigFile > configs = yamlFiles.map ((e) {
78
201
return ConfigFile (
79
202
e.path.getFileNameNoExtension (),
80
203
e.parent.path,
81
- YamlParser .fromYamlString ( e.readAsStringSync () ),
204
+ YamlParser .fromYamlString (e.readAsStringSync ()),
82
205
);
83
206
}).toList ();
84
207
85
208
Graph <ConfigFile ?> graph = Graph <ConfigFile ?>(
86
- name: ( c ) => c? .config.name ?? 'null' ,
87
- keepAlive: ( f ) => f != null ,
209
+ name: (c ) => c? .config.name ?? 'null' ,
210
+ keepAlive: (f ) => f != null ,
88
211
);
89
212
90
- void buildPartGraph ( List <ConfigFile > input, [ ConfigFile ? from ] ) {
91
-
92
- for ( var c in input ) {
93
-
94
- var parts = configs. where ((e) => c.config.partFiles. contains ( e.config.name )) .toList ();
213
+ void buildPartGraph (List <ConfigFile > input, [ConfigFile ? from] ) {
214
+ for ( var c in input) {
215
+ var parts = configs
216
+ . where ((e) => c.config.partFiles. contains (e.config.name))
217
+ .toList ();
95
218
96
- graph.addEdge ( from, c );
219
+ graph.addEdge (from, c);
97
220
98
- buildPartGraph ( parts, c );
221
+ buildPartGraph (parts, c);
99
222
}
100
223
}
101
224
102
- void mergeConfigs ( Set <ConfigFile ?> set , List <String > handled ) {
103
-
104
- for ( var a in set ) {
105
-
106
- if ( a == null || handled.contains ( a.config.name ) ) {
225
+ void mergeConfigs (Set <ConfigFile ?> set , List <String > handled) {
226
+ for (var a in set ) {
227
+ if (a == null || handled.contains (a.config.name)) {
107
228
continue ;
108
229
}
109
230
110
- Set <ConfigFile ?> parts = graph.from ( a );
231
+ Set <ConfigFile ?> parts = graph.from (a );
111
232
112
- mergeConfigs ( parts, handled );
233
+ mergeConfigs (parts, handled);
113
234
114
- var to = graph.to ( a );
235
+ var to = graph.to (a );
115
236
116
- for ( var t in to ) {
117
- if ( t != null ) {
237
+ for (var t in to) {
238
+ if (t != null ) {
118
239
print ('Merged ${a .config .name } --> ${t .config .name }' );
119
240
t.config = t.config + a.config;
120
- handled.add ( a.config.name );
241
+ handled.add (a.config.name);
121
242
}
122
243
}
123
244
}
124
245
}
125
246
126
- buildPartGraph ( List .from ( configs ) );
127
-
128
- var baseGraph = graph.from ( null );
129
-
130
- graph.delete ( null );
247
+ buildPartGraph (List .from (configs));
131
248
132
- print ( '\n ---Built Configuration Graph---' );
133
- print ( graph.toDebugString () );
249
+ var baseGraph = graph.from (null );
250
+
251
+ graph.delete (null );
252
+
253
+ print ('\n ---Built Configuration Graph---' );
254
+ print (graph.toDebugString ());
134
255
135
256
List <String > track = [];
136
257
137
- print ( '\n ---Merging Configurations---' );
138
- for ( var node in baseGraph ) {
139
- mergeConfigs ( graph.from ( node ), track );
258
+ print ('\n ---Merging Configurations---' );
259
+ for (var node in baseGraph) {
260
+ mergeConfigs (graph.from (node), track);
140
261
}
141
262
142
- print ( '\n ---Generating Dart Classes---' );
143
- for ( var file in configs.where (( c ) => ! track.contains ( c.config.name )) ) {
263
+ print ('\n ---Generating Dart Classes---' );
264
+ for (var file in configs.where ((c) => ! track.contains (c.config.name))) {
265
+ String outputFilePath =
266
+ '${file .directory }${Platform .pathSeparator }${file .name }.config.dart' ;
144
267
145
- String outputFilePath = '${file .directory }${Platform .pathSeparator }${file .name }.config.dart' ;
146
-
147
- var result = ProcessedConfig ( file.config.name.camelCase.capitalized, file.config );
268
+ var result =
269
+ ProcessedConfig (file.config.name.camelCase.capitalized, file.config);
148
270
149
271
FileUtils .writeFile (
150
272
path: outputFilePath,
151
- content: DartFormatter ().format ( await result.write () ),
273
+ content: DartFormatter ().format (await result.write ()),
152
274
// content: await result.write(),
153
275
);
154
276
155
- print ( outputFilePath );
277
+ print (outputFilePath);
156
278
}
157
279
158
- print ( '\n *****Configurator Has Configured!*****' );
159
-
280
+ print ('\n *****Configurator Has Configured!*****' );
160
281
}
161
282
162
283
Future <void > watchConfiguration ({
163
284
required List <FileSystemEntity > files,
164
285
}) async {
165
-
166
286
StreamController sc = StreamController <FileSystemEvent >();
167
287
168
288
List <String > watchDirs = [];
169
289
170
- for ( var file in files ) {
171
- if ( ! watchDirs.contains ( file.parent.path ) ) {
172
- file.parent.watch ( events: FileSystemEvent .all ).listen ( sc.sink.add );
173
- print ( 'Watching: ${file .parent .path }' );
174
- watchDirs.add ( file.parent.path );
290
+ for (var file in files) {
291
+ if (! watchDirs.contains (file.parent.path) ) {
292
+ file.parent.watch (events: FileSystemEvent .all).listen (sc.sink.add);
293
+ print ('Watching: ${file .parent .path }' );
294
+ watchDirs.add (file.parent.path);
175
295
}
176
296
}
177
297
@@ -184,15 +304,12 @@ Future<void> watchConfiguration({
184
304
stdout.write ('\r -> Watching for Changes... \r ' );
185
305
await for (final event in sc.stream) {
186
306
if (event.path.endsWith ('.config.yaml' )) {
187
-
188
307
stdout.write ('\r -> Generating For ${event .path }\r ' );
189
308
190
- final newFiles = Directory .current
191
- .listSync (recursive: true )
192
- .where ((item) {
193
- return item is File && item.path.endsWith ( '.config.yaml' );
194
- })
195
- .toList ();
309
+ final newFiles =
310
+ Directory .current.listSync (recursive: true ).where ((item) {
311
+ return item is File && item.path.endsWith ('.config.yaml' );
312
+ }).toList ();
196
313
197
314
await generateConfigurations (
198
315
files: newFiles,
@@ -213,4 +330,4 @@ extension on String {
213
330
String getFileNameNoExtension () {
214
331
return PathUtils .getFileNameNoExtension (this );
215
332
}
216
- }
333
+ }
0 commit comments