Skip to content

Commit 1f94cd6

Browse files
author
cgiuliani
committed
feat: add support for global color defs
1 parent d439c4d commit 1f94cd6

File tree

3 files changed

+191
-72
lines changed

3 files changed

+191
-72
lines changed

Diff for: configurator/bin/configurator.dart

+187-70
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,65 @@
11
import 'dart:async';
2+
import 'dart:convert';
23
import 'dart:io';
4+
import 'package:collection/collection.dart';
35
import 'package:configurator/configurator.dart';
46
import 'package:configurator/src/models/processed_config.dart';
57
import 'package:configurator/src/utils/string_ext.dart';
68
import 'package:dart_style/dart_style.dart';
79
import 'package:slang/builder/utils/path_utils.dart';
10+
import 'package:yaml/yaml.dart';
11+
import 'package:yaml_edit/yaml_edit.dart';
812
import 'config_file.dart';
913
import 'file_utils.dart';
1014
import 'graph.dart';
15+
import 'package:source_span/source_span.dart';
1116

1217
/// To run this:
1318
/// -> 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(',')
1931
: [];
2032

21-
print( '\n*****Configurator Starting!*****' );
33+
print('\n*****Configurator Starting!*****');
2234

23-
final stopwatch = Stopwatch();
35+
List<FileSystemEntity> files = findConfigurations(filters);
2436

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+
);
2753
}
2854

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(
3063
rootDirectory: Directory.current,
3164
ignoreTopLevelDirectories: {
3265
'.fvm',
@@ -40,15 +73,105 @@ Future<void> main(List<String> arguments) async {
4073
'web',
4174
},
4275
).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());
4679

4780
return isConfig && matchesFilter;
4881
}).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}:'));
49126

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'));
52175

53176
if (watch) {
54177
await watchConfiguration(
@@ -71,107 +194,104 @@ Future<void> generateConfigurations({
71194
final List<String> yamlInput = files.map((e) => e.path).toList();
72195

73196
// 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();
75198

76199
// Convert to ConfigFiles (dir & config)
77200
List<ConfigFile> configs = yamlFiles.map((e) {
78201
return ConfigFile(
79202
e.path.getFileNameNoExtension(),
80203
e.parent.path,
81-
YamlParser.fromYamlString( e.readAsStringSync() ),
204+
YamlParser.fromYamlString(e.readAsStringSync()),
82205
);
83206
}).toList();
84207

85208
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,
88211
);
89212

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();
95218

96-
graph.addEdge( from, c );
219+
graph.addEdge(from, c);
97220

98-
buildPartGraph( parts, c );
221+
buildPartGraph(parts, c);
99222
}
100223
}
101224

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)) {
107228
continue;
108229
}
109230

110-
Set<ConfigFile?> parts = graph.from( a );
231+
Set<ConfigFile?> parts = graph.from(a);
111232

112-
mergeConfigs( parts, handled );
233+
mergeConfigs(parts, handled);
113234

114-
var to = graph.to( a );
235+
var to = graph.to(a);
115236

116-
for ( var t in to ) {
117-
if ( t != null ) {
237+
for (var t in to) {
238+
if (t != null) {
118239
print('Merged ${a.config.name} --> ${t.config.name}');
119240
t.config = t.config + a.config;
120-
handled.add( a.config.name );
241+
handled.add(a.config.name);
121242
}
122243
}
123244
}
124245
}
125246

126-
buildPartGraph( List.from( configs ) );
127-
128-
var baseGraph = graph.from( null );
129-
130-
graph.delete( null );
247+
buildPartGraph(List.from(configs));
131248

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());
134255

135256
List<String> track = [];
136257

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);
140261
}
141262

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';
144267

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);
148270

149271
FileUtils.writeFile(
150272
path: outputFilePath,
151-
content: DartFormatter().format( await result.write() ),
273+
content: DartFormatter().format(await result.write()),
152274
// content: await result.write(),
153275
);
154276

155-
print( outputFilePath );
277+
print(outputFilePath);
156278
}
157279

158-
print( '\n*****Configurator Has Configured!*****' );
159-
280+
print('\n*****Configurator Has Configured!*****');
160281
}
161282

162283
Future<void> watchConfiguration({
163284
required List<FileSystemEntity> files,
164285
}) async {
165-
166286
StreamController sc = StreamController<FileSystemEvent>();
167287

168288
List<String> watchDirs = [];
169289

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);
175295
}
176296
}
177297

@@ -184,15 +304,12 @@ Future<void> watchConfiguration({
184304
stdout.write('\r -> Watching for Changes... \r');
185305
await for (final event in sc.stream) {
186306
if (event.path.endsWith('.config.yaml')) {
187-
188307
stdout.write('\r -> Generating For ${event.path}\r');
189308

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();
196313

197314
await generateConfigurations(
198315
files: newFiles,
@@ -213,4 +330,4 @@ extension on String {
213330
String getFileNameNoExtension() {
214331
return PathUtils.getFileNameNoExtension(this);
215332
}
216-
}
333+
}

Diff for: configurator/pubspec.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ dependencies:
1111
# parse_color: ^0.0.1
1212
collection: ^1.16.0
1313
yaml: ^3.1.1
14+
yaml_edit: ^2.0.3
15+
source_span: ^1.9.1
1416
meta: ^1.7.0
1517
slang: 3.0.0
1618
rxdart: ^0.27.5

Diff for: example/pubspec.lock

+2-2
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ packages:
140140
path: "../configurator"
141141
relative: true
142142
source: path
143-
version: "0.0.2"
143+
version: "0.0.7"
144144
configurator_annotations:
145145
dependency: "direct main"
146146
description:
@@ -161,7 +161,7 @@ packages:
161161
path: "../configurator_flutter"
162162
relative: true
163163
source: path
164-
version: "0.0.1"
164+
version: "0.0.7"
165165
convert:
166166
dependency: transitive
167167
description:

0 commit comments

Comments
 (0)