Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/envied_generator/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ final class EnviedGenerator extends GeneratorForAnnotation<Envied> {
throw InvalidGenerationSourceError(error, element: element);
}
},
buildStep: buildStep,
);

final DartEmitter emitter = DartEmitter(useNullSafetySyntax: true);
Expand Down
45 changes: 40 additions & 5 deletions packages/envied_generator/lib/src/load_envs.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:convert' show LineSplitter;
import 'dart:io' show File;

import 'package:build/build.dart';
import 'package:envied_generator/src/env_val.dart';
import 'package:envied_generator/src/parser.dart';

Expand All @@ -10,21 +12,23 @@ import 'package:envied_generator/src/parser.dart';
/// [onError] function.
Future<Map<String, EnvVal>> loadEnvs(
String path,
void Function(String) onError,
) => loadEnvsFromPaths(<String>[path], onError);
void Function(String) onError, {
BuildStep? buildStep,
}) => loadEnvsFromPaths(<String>[path], onError, buildStep: buildStep);

/// Load and merge environment variables from [paths].
///
/// Files are parsed in order. Later files override earlier files, while
/// duplicate keys within a single file keep the first parsed value.
Future<Map<String, EnvVal>> loadEnvsFromPaths(
Iterable<String> paths,
void Function(String) onError,
) async {
void Function(String) onError, {
BuildStep? buildStep,
}) async {
final Map<String, EnvVal> envs = {};

for (final String path in paths) {
envs.addAll(await _loadEnv(path, onError, env: envs));
envs.addAll(await _loadEnv(path, onError, env: envs, buildStep: buildStep));
}

return envs;
Expand All @@ -34,7 +38,23 @@ Future<Map<String, EnvVal>> _loadEnv(
String path,
void Function(String) onError, {
required Map<String, EnvVal> env,
BuildStep? buildStep,
}) async {
final AssetId? assetId = _assetIdForPath(path, buildStep);

if (assetId != null) {
final List<String> lines = [];
if (await buildStep!.canRead(assetId)) {
lines.addAll(
const LineSplitter().convert(await buildStep.readAsString(assetId)),
);
} else {
onError("Environment variable file doesn't exist at `$path`.");
}

return Parser.parse(lines, env: env);
}

final File file = File.fromUri(Uri.file(path));

final List<String> lines = [];
Expand All @@ -46,3 +66,18 @@ Future<Map<String, EnvVal>> _loadEnv(

return Parser.parse(lines, env: env);
}

AssetId? _assetIdForPath(String path, BuildStep? buildStep) {
if (buildStep == null || File(path).isAbsolute) {
return null;
}

try {
return AssetId(buildStep.inputId.package, path);
} on ArgumentError {
return null;
} on NoSuchMethodError {
// source_gen_test passes a mock BuildStep that does not expose inputId.
return null;
}
}
1 change: 1 addition & 0 deletions packages/envied_generator/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dependencies:
source_gen: ^4.1.1

dev_dependencies:
build_test: ^3.5.15
lints: ^6.0.0
source_gen_test: ^1.3.3
test: ^1.28.0
Expand Down
94 changes: 94 additions & 0 deletions packages/envied_generator/test/workspace_resolution_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'package:build/build.dart';
import 'package:build_test/build_test.dart';
import 'package:envied_generator/builder.dart';
import 'package:test/test.dart';

void main() {
test('loads the default env file from the input package', () async {
final AssetId input = AssetId('envied_generator', 'lib/default_env.dart');
final AssetId env = AssetId('envied_generator', '.env');

final TestBuilderResult result = await _build(input, <String, Object>{
'$input': '''
import 'package:envied/envied.dart';

@Envied(requireEnvFile: true)
abstract class DefaultEnv {
@EnviedField()
static const String? apiUrl = null;
}
''',
'$env': 'apiUrl=https://package.example',
});

expect(result.succeeded, isTrue);
expect(result.readerWriter.testing.inputsTracked, contains(env));
expect(
result.readerWriter.testing.readString(
input.changeExtension('.envied.g.part'),
),
contains("static const String apiUrl = 'https://package.example';"),
);
});

test('loads inherited env files from the input package', () async {
final AssetId input = AssetId('envied_generator', 'lib/inherited_env.dart');
final AssetId defaults = AssetId('envied_generator', 'config/defaults.env');
final AssetId env = AssetId('envied_generator', 'config/local.env');

final TestBuilderResult result = await _build(input, <String, Object>{
'$input': '''
import 'package:envied/envied.dart';

@Envied(
path: 'config/local.env',
inheritFrom: ['config/defaults.env'],
requireEnvFile: true,
)
abstract class InheritedEnv {
@EnviedField()
static const String? apiUrl = null;

@EnviedField()
static const String? feature = null;
}
''',
'$defaults': '''
apiUrl=https://default.example
feature=off
''',
'$env': 'apiUrl=https://local.example',
});

expect(result.succeeded, isTrue);
expect(result.readerWriter.testing.inputsTracked, contains(defaults));
expect(result.readerWriter.testing.inputsTracked, contains(env));
expect(
result.readerWriter.testing.readString(
input.changeExtension('.envied.g.part'),
),
allOf(
contains("static const String apiUrl = 'https://local.example';"),
contains("static const String feature = 'off';"),
),
);
});
}

Future<TestBuilderResult> _build(
AssetId input,
Map<String, Object> sourceAssets,
) async {
final TestReaderWriter readerWriter = TestReaderWriter(
rootPackage: input.package,
);
await readerWriter.testing.loadIsolateSources();

return testBuilder(
enviedBuilder(BuilderOptions.empty),
sourceAssets,
rootPackage: input.package,
readerWriter: readerWriter,
flattenOutput: true,
);
}