-
-
Notifications
You must be signed in to change notification settings - Fork 371
Feature/generator yaml support #772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from 3 commits
854410e
cfa45f1
7505368
a5645ac
b82218d
a107608
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,6 +4,7 @@ import 'dart:io'; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:args/args.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:path/path.dart' as path; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import 'package:yaml/yaml.dart'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const _preservedKeywords = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'few', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -43,35 +44,45 @@ GenerateOptions _generateOption(List<String> args) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ArgParser _generateArgParser(GenerateOptions? generateOptions) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var parser = ArgParser(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption('source-dir', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'S', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'resources/langs', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.sourceDir = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Folder containing localization files'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption('source-file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 's', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.sourceFile = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'File to use for localization'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption('output-dir', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'O', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'lib/generated', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.outputDir = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Output folder stores for the generated file'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption('output-file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'o', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'codegen_loader.g.dart', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.outputFile = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Output file name'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption('format', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'f', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'json', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.format = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Support json or keys formats', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allowed: ['json', 'keys']); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'source-dir', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'S', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'resources/langs', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.sourceDir = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Folder containing localization files', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'source-file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 's', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.sourceFile = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'File to use for localization', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'output-dir', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'O', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'lib/generated', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.outputDir = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Output folder stores for the generated file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'output-file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'o', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'codegen_loader.g.dart', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.outputFile = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Output file name', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addOption( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'format', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| abbr: 'f', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defaultsTo: 'json', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| callback: (String? x) => generateOptions!.format = x, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| help: 'Support json or keys formats', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allowed: ['json', 'keys'], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| parser.addFlag( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'skip-unnecessary-keys', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -104,8 +115,9 @@ void handleLangFiles(GenerateOptions options) async { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final source = Directory.fromUri(Uri.parse(options.sourceDir!)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final output = Directory.fromUri(Uri.parse(options.outputDir!)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final sourcePath = Directory(path.join(current.path, source.path)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final outputPath = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Directory(path.join(current.path, output.path, options.outputFile)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final outputPath = Directory( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| path.join(current.path, output.path, options.outputFile), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| final outputPath = Directory( | |
| path.join(current.path, output.path, options.outputFile), | |
| ); | |
| final outputFile = File( | |
| path.join(current.path, output.path, options.outputFile), | |
| ); |
🤖 Prompt for AI Agents
In bin/generate.dart around lines 118 to 121, outputPath is constructed as a
Directory but it actually contains a file path (includes options.outputFile);
change it to a File (use File(path.join(...))) to reflect a file target, update
the call site at line 143 to pass a File instead of Directory, and adjust the
generateFile function signature to accept a File (and update any
Directory-specific APIs inside generateFile to File APIs).
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Filter by selected format and ensure only files are processed.
Currently mixes JSON with YAML when format='yaml' and may include directories. Make filtering deterministic and case-insensitive.
- //filtering format
- files = files.where((f) => f.path.contains(RegExp(r'\.(json|yaml|yml)$'))).toList();
+ // Filter files based on requested format; keep only regular files.
+ files = files
+ .whereType<File>()
+ .where((f) {
+ final p = f.path.toLowerCase();
+ switch (options.format) {
+ case 'json':
+ return p.endsWith('.json');
+ case 'yaml':
+ return p.endsWith('.yaml') || p.endsWith('.yml');
+ case 'keys':
+ return p.endsWith('.json') || p.endsWith('.yaml') || p.endsWith('.yml');
+ default:
+ return false;
+ }
+ })
+ .toList();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| //filtering format | |
| files = files.where((f) => f.path.contains('.json')).toList(); | |
| files = files.where((f) => f.path.contains(RegExp(r'\.(json|yaml|yml)$'))).toList(); | |
| } | |
| // Filter files based on requested format; keep only regular files. | |
| files = files | |
| .whereType<File>() | |
| .where((f) { | |
| final p = f.path.toLowerCase(); | |
| switch (options.format) { | |
| case 'json': | |
| return p.endsWith('.json'); | |
| case 'yaml': | |
| return p.endsWith('.yaml') || p.endsWith('.yml'); | |
| case 'keys': | |
| return p.endsWith('.json') || p.endsWith('.yaml') || p.endsWith('.yml'); | |
| default: | |
| return false; | |
| } | |
| }) | |
| .toList(); | |
| } |
🤖 Prompt for AI Agents
In bin/generate.dart around lines 125-127, the current filter mixes JSON and
YAML when format='yaml' and can include directories; update the filtering to
first exclude non-files using FileSystemEntity.isFileSync(path) and then match
extensions deterministically: build the allowed extensions based on the selected
format (e.g., ['json'] for 'json', ['yaml','yml'] for 'yaml'), compile a RegExp
that matches the extension at the end of the path with caseSensitive: false, and
filter files by both being a real file and matching that regex.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Align generateFile API with a File destination.
Update signature and usage to accept File instead of Directory.
-void generateFile(
- List<FileSystemEntity> files,
- Directory outputPath,
- GenerateOptions options,
-) async {
- var generatedFile = File(outputPath.path);
+void generateFile(
+ List<FileSystemEntity> files,
+ File outputFile,
+ GenerateOptions options,
+) async {
+ var generatedFile = outputFile;
if (!generatedFile.existsSync()) {
generatedFile.createSync(recursive: true);
}And at Line 143:
- generateFile(files, outputPath, options);
+ generateFile(files, outputFile, options);And at Line 186:
- stdout.writeln('All done! File generated in ${outputPath.path}');
+ stdout.writeln('All done! File generated in ${outputFile.path}');📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| void generateFile( | |
| List<FileSystemEntity> files, | |
| Directory outputPath, | |
| GenerateOptions options, | |
| ) async { | |
| var generatedFile = File(outputPath.path); | |
| if (!generatedFile.existsSync()) { | |
| // Updated signature to take a File instead of Directory | |
| void generateFile( | |
| List<FileSystemEntity> files, | |
| File outputFile, | |
| GenerateOptions options, | |
| ) async { | |
| var generatedFile = outputFile; | |
| if (!generatedFile.existsSync()) { | |
| generatedFile.createSync(recursive: true); | |
| } | |
| // …rest of the implementation… | |
| } |
| void generateFile( | |
| List<FileSystemEntity> files, | |
| Directory outputPath, | |
| GenerateOptions options, | |
| ) async { | |
| var generatedFile = File(outputPath.path); | |
| if (!generatedFile.existsSync()) { | |
| // At Line 143: pass the File instead of a Directory | |
| generateFile(files, outputFile, options); |
| void generateFile( | |
| List<FileSystemEntity> files, | |
| Directory outputPath, | |
| GenerateOptions options, | |
| ) async { | |
| var generatedFile = File(outputPath.path); | |
| if (!generatedFile.existsSync()) { | |
| // At Line 186: reference outputFile.path | |
| stdout.writeln('All done! File generated in ${outputFile.path}'); |
🤖 Prompt for AI Agents
In bin/generate.dart around lines 160-166 (and also update references at lines
143 and 186), the generateFile function currently accepts a Directory but should
accept a File destination; change the function signature parameter type from
Directory outputPath to File outputFile (and rename usages accordingly), replace
any calls to outputPath.path with outputFile.path, update existence and
parent-directory handling to use outputFile.existsSync() and
outputFile.parent.createSync(recursive: true) as needed, and update the two call
sites at lines 143 and 186 to pass a File object instead of a Directory.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Keys generation reads only YAML; restore JSON support and validate top-level structure.
Use a shared parser that detects by extension and ensures a top-level map.
- // Parse YAML file
- final yamlString = await fileData.readAsString();
- final yamlData = loadYaml(yamlString);
- Map<String, dynamic> translations = _yamlToMap(yamlData);
+ // Parse file (.json, .yml, .yaml)
+ final Map<String, dynamic> translations = await _parseTranslations(fileData);Additional helper to add (see snippet below in this comment thread).
Add this helper outside the function (near _yamlToMap):
// Parses .json, .yml, .yaml into a Map<String, dynamic>.
Future<Map<String, dynamic>> _parseTranslations(File file) async {
final ext = path.extension(file.path).toLowerCase();
final content = await file.readAsString();
if (ext == '.json') {
final decoded = json.decode(content);
if (decoded is! Map) {
throw FormatException('Top-level JSON must be an object');
}
return Map<String, dynamic>.from(decoded as Map);
}
final yamlRoot = loadYaml(content);
final map = _yamlToMap(yamlRoot);
if (map.isEmpty && yamlRoot is! YamlMap) {
throw FormatException('Top-level YAML must be a mapping');
}
return map;
}🤖 Prompt for AI Agents
In bin/generate.dart around lines 189 to 208, the _writeKeys function currently
reads only YAML files and directly calls loadYaml, so JSON input and top-level
structure validation are missing; replace the inline YAML-only parsing with a
shared async parser that checks the file extension ('.json' vs '.yml'/' .yaml'),
decodes JSON into a Map and throws a FormatException if the decoded top-level is
not a Map, or for YAML uses loadYaml then converts via _yamlToMap and throws if
the YAML root is not a mapping; add this helper function outside _writeKeys
(near _yamlToMap) and call it from _writeKeys instead of the current YAML-only
code so both JSON and YAML are supported and validated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keys generation reads YAML only — add shared parser to support JSON and validate top-level.
Without this, -f keys fails for .json sources and lacks structure checks.
- Map<String, dynamic> translations =
- json.decode(json.encode(loadYaml(await fileData.readAsString())));
+ final Map<String, dynamic> translations = await _parseTranslations(fileData);Add this helper (place near _resolve):
// Parses .json, .yaml, .yml into Map<String, dynamic> and validates top-level map.
Future<Map<String, dynamic>> _parseTranslations(File file) async {
final ext = path.extension(file.path).toLowerCase();
final content = await file.readAsString();
if (ext == '.json') {
final decoded = json.decode(content);
if (decoded is! Map) {
throw const FormatException('Top-level JSON must be an object');
}
return Map<String, dynamic>.from(decoded as Map);
}
// YAML path
final yamlRoot = loadYaml(content);
// Normalize YamlMap/YamlList into JSON-friendly Map/List
final normalized = json.decode(json.encode(yamlRoot));
if (normalized is! Map) {
throw const FormatException('Top-level YAML must be a mapping');
}
return Map<String, dynamic>.from(normalized as Map);
}🤖 Prompt for AI Agents
In bin/generate.dart around lines 187 to 191, the current code reads YAML only
and directly decodes a YAML structure into a Map which breaks when input is JSON
and does not validate top-level structure; add a helper method named
_parseTranslations placed near _resolve that: reads the file bytes, inspects
path.extension(file.path).toLowerCase(), for '.json' uses json.decode and throws
a FormatException if the decoded value is not a Map, for YAML ('.yaml' or
'.yml') uses loadYaml then normalizes the YamlMap/YamlList to JSON-friendly
structures via json.encode/json.decode, validates the normalized value is a Map,
converts and returns Map<String, dynamic>; then replace the current YAML-only
logic at lines 187–191 to call await _parseTranslations(fileData) and use its
returned Map.
Uh oh!
There was an error while loading. Please reload this page.