generateDartCode static method
Generate Dart code from lock file
Implementation
static Future<String> generateDartCode({
String? lockPath,
String? configPath,
String? workingDirectory,
}) async {
const defaultLockFile = 'assetx.lock';
final lockFile = lockPath ?? defaultLockFile;
final lock = await LockFile.load(lockFile);
// Get current package name from pubspec.yaml
final workDir = workingDirectory ?? Directory.current.path;
final packageName = await _getPackageName(workDir);
// Group files by type for generator processing
final filesByType = <String, List<FileConfig>>{};
for (final fileConfig in lock.files) {
filesByType.putIfAbsent(fileConfig.type, () => []).add(fileConfig);
}
final buffer = StringBuffer();
// Add required imports
buffer.writeln(
'// ignore_for_file: constant_identifier_names, non_constant_identifier_names, camel_case_types',
);
buffer.writeln();
buffer.writeln('import \'dart:convert\';');
buffer.writeln('import \'package:flutter/material.dart\';');
buffer.writeln('import \'package:flutter/services.dart\';');
buffer.writeln('import \'package:assetxf/assetxf.dart\';');
buffer.writeln();
// Group files by their actual folder paths (use hash-based keys to avoid name collisions)
final folderGroups = <String, List<FileConfig>>{};
final folderKeyToPath = <String, String>{};
for (final fileConfig in lock.files) {
final folderPath = path.dirname(fileConfig.fullPath);
final relativeFolderPath = path.relative(folderPath, from: workDir);
// Generate unique key for folder grouping
final folderKey = IdentifierUtils.createFolderKey(relativeFolderPath);
folderKeyToPath[folderKey] = relativeFolderPath;
folderGroups.putIfAbsent(folderKey, () => []).add(fileConfig);
}
// Generate constants for all assets first - delegate to generators
for (final entry in filesByType.entries) {
final type = entry.key;
final files = entry.value;
final generator = GeneratorRegistry.getGenerator(type);
if (generator != null) {
final result = generator.generateCode(files);
buffer.writeln(result.partFileContent);
}
}
// Generate folder-based classes
for (final entry in folderGroups.entries) {
final folderKey = entry.key;
final filesInFolder = entry.value;
// Get original path and generate unique class name
final originalPath = folderKeyToPath[folderKey]!;
final folderClassName = IdentifierUtils.createUniqueClassName(
originalPath,
);
// Get all accessors from generators once
final allAccessors = <FileAccessor>[];
final filesByTypeInFolder = <String, List<FileConfig>>{};
for (final file in filesInFolder) {
filesByTypeInFolder.putIfAbsent(file.type, () => []).add(file);
}
for (final typeEntry in filesByTypeInFolder.entries) {
final type = typeEntry.key;
final files = typeEntry.value;
final generator = GeneratorRegistry.getGenerator(type);
if (generator != null) {
final accessors = generator.generateAccessors(files);
allAccessors.addAll(accessors);
}
}
// Generate _files class using generator-provided accessors
buffer.writeln('class ${folderClassName}_files {');
buffer.writeln(' const ${folderClassName}_files();');
buffer.writeln();
for (final accessor in allAccessors) {
buffer.writeln(' ${accessor.accessorCode}');
}
buffer.writeln('}');
buffer.writeln();
// Generate _filepaths class using generator-provided file path accessors
buffer.writeln('class ${folderClassName}_filepaths {');
buffer.writeln(' const ${folderClassName}_filepaths();');
buffer.writeln();
for (final accessor in allAccessors) {
buffer.writeln(' ${accessor.filePathAccessor}');
}
buffer.writeln('}');
buffer.writeln();
// Generate main folder class
buffer.writeln('class $folderClassName {');
buffer.writeln(' const $folderClassName();');
buffer.writeln();
buffer.writeln(
' ${folderClassName}_filepaths get \$paths => const ${folderClassName}_filepaths();',
);
buffer.writeln(
' ${folderClassName}_files get \$files => const ${folderClassName}_files();',
);
buffer.writeln('}');
buffer.writeln();
}
// Generate package-named class that contains all folder instances
if (folderKeyToPath.isNotEmpty) {
final packageClassName = IdentifierUtils.createValidClassName(
packageName,
);
buffer.writeln('class $packageClassName {');
buffer.writeln(' const $packageClassName();');
buffer.writeln();
// Generate getters using readable folder names but pointing to hash-based classes
final processedFolderNames = <String>{};
for (final entry in folderKeyToPath.entries) {
final folderPath = entry.value;
final folderName = path.basename(folderPath);
// Use readable folder name for getter
final getterName = IdentifierUtils.createValidIdentifier(folderName);
// Use hash-based class name for type
final folderClassName = IdentifierUtils.createUniqueClassName(
folderPath,
);
// Handle duplicate folder names by numbering them
var finalGetterName = getterName;
var counter = 1;
while (processedFolderNames.contains(finalGetterName)) {
finalGetterName = '${getterName}$counter';
counter++;
}
buffer.writeln(
' $folderClassName get $finalGetterName => const $folderClassName();',
);
processedFolderNames.add(finalGetterName);
}
buffer.writeln('}');
buffer.writeln();
// Generate extension on AssetX with package-named class instance
buffer.writeln('extension AssetXGenerated on AssetX {');
final packageGetterName = IdentifierUtils.createValidIdentifier(
packageName,
);
buffer.writeln(
' $packageClassName get $packageGetterName => const $packageClassName();',
);
buffer.writeln('}');
}
return buffer.toString();
}