88 lines
2.5 KiB
Dart
88 lines
2.5 KiB
Dart
part of dotenv;
|
|
|
|
class DotenvParser {
|
|
static const _singleQuot = "'";
|
|
static const _keyword = 'export';
|
|
|
|
static final _comment = RegExp(r'''#.*(?:[^'"])$''');
|
|
static final _surroundQuotes = RegExp(r'''^(['"])(.*)\1$''');
|
|
static final _bashVar = RegExp(r'(?:\\)?(\$)(?:{)?([a-zA-Z_][\w]*)+(?:})?');
|
|
|
|
// constructor for parser
|
|
const DotenvParser();
|
|
|
|
Map<String, String> parse(Iterable<String> lines) {
|
|
var out = <String, String>{};
|
|
lines.forEach((line) {
|
|
var kv = parseOne(line, env: out);
|
|
if (kv.isEmpty) return;
|
|
out.putIfAbsent(kv.keys.single, () => kv.values.single);
|
|
});
|
|
return out;
|
|
}
|
|
|
|
/// Parses a single line into a key-value pair.
|
|
@visibleForTesting
|
|
Map<String, String> parseOne(String line,
|
|
{Map<String, String> env: const {}}) {
|
|
var stripped = strip(line);
|
|
if (!_isValid(stripped)) return {};
|
|
|
|
var idx = stripped.indexOf('=');
|
|
var lhs = stripped.substring(0, idx);
|
|
var k = swallow(lhs);
|
|
if (k.isEmpty) return {};
|
|
|
|
var rhs = stripped.substring(idx + 1, stripped.length).trim();
|
|
var quotChar = surroundingQuote(rhs);
|
|
var v = unquote(rhs);
|
|
|
|
if (quotChar == _singleQuot) {
|
|
return {k: v};
|
|
}
|
|
|
|
return {k: interpolate(v, env)};
|
|
}
|
|
|
|
/// Substitutes $bash_vars in [val] with values from [env].
|
|
@visibleForTesting
|
|
String interpolate(String val, Map<String, String> env) =>
|
|
val.replaceAllMapped(_bashVar, (m) {
|
|
var k = m.group(2)!;
|
|
return (!_has(env, k)) ? _tryPlatformEnv(k) ?? '' : env[k] ?? '';
|
|
});
|
|
|
|
/// If [val] is wrapped in single or double quotes, returns the quote character.
|
|
/// Otherwise, returns the empty string.
|
|
@visibleForTesting
|
|
String surroundingQuote(String val) {
|
|
if (!_surroundQuotes.hasMatch(val)) return '';
|
|
return _surroundQuotes.firstMatch(val)!.group(1)!;
|
|
}
|
|
|
|
/// Removes quotes (single or double) surrounding a value.
|
|
@visibleForTesting
|
|
String unquote(String val) =>
|
|
val.replaceFirstMapped(_surroundQuotes, (m) => m[2]!).trim();
|
|
|
|
/// Strips comments (trailing or whole-line).
|
|
@visibleForTesting
|
|
String strip(String line) => line.replaceAll(_comment, '').trim();
|
|
|
|
/// Omits 'export' keyword.
|
|
@visibleForTesting
|
|
String swallow(String line) => line.replaceAll(_keyword, '').trim();
|
|
|
|
bool _isValid(String s) => s.isNotEmpty && s.contains('=');
|
|
|
|
bool _has(Map<String, String> map, String key) =>
|
|
map.containsKey(key) && map[key] != null;
|
|
|
|
String? _tryPlatformEnv(String key) {
|
|
if (!_has(Platform.environment, key)) {
|
|
return '';
|
|
}
|
|
return Platform.environment[key];
|
|
}
|
|
}
|