diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7a565..ec9cda1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ This project follows [pub-flavored semantic versioning][pub-semver]. ([more][pub [pub-semver]: https://www.dartlang.org/tools/pub/versioning.html#semantic-versions [pub-semver-readme]: https://pub.dartlang.org/packages/pub_semver +## 4.3.0 + +- [feat] Moved `lib/src/dotenv.dart` and `lib/src/parser.dart` into `lib/` and deleted the empty `lib/src` directory. With this change, the user can now import the `Parser` class without importing files from the `lib/src` directory, because that is considered a bad practice. [See lint implementation_imports](https://dart.dev/tools/linter-rules/implementation_imports) +- [deps] added package `universal_io` +- [fix] typo, renamed `Parser._singleQuot` to `Parser._singleQuote` +- [fix] replaced dependencies of `dart:io` in `lib/dotenv.dart` and `lib/parser.dart` with `package:universal_io/io.dart`, this should make the package compatible with flutter web and fix [#33][] + ## 4.2.0 - [feat] add optional parameter `quiet` to `DotEnv` constructor @@ -102,3 +109,4 @@ Initial release. [#16]: https://github.com/mockturtl/dotenv/issues/16 [#21]: https://github.com/mockturtl/dotenv/pull/21 [#27]: https://github.com/mockturtl/dotenv/pull/27 +[#33]: https://github.com/mockturtl/dotenv/issues/33 \ No newline at end of file diff --git a/example/example.dart b/example/example.dart index 4474bea..41b0060 100644 --- a/example/example.dart +++ b/example/example.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:dotenv/dotenv.dart'; void main() { @@ -19,4 +17,4 @@ void main() { p('your home directory is still: ${env['HOME']}'); } -p(String msg) => stdout.writeln(msg); +p(String msg) => print(msg); diff --git a/lib/dotenv.dart b/lib/dotenv.dart index ef29cc9..c0530b8 100644 --- a/lib/dotenv.dart +++ b/lib/dotenv.dart @@ -1,3 +1,12 @@ +import 'package:meta/meta.dart'; +import 'package:universal_io/io.dart'; + +import 'parser.dart'; + +/// Loads key-value pairs from a file into a [Map]. +/// +/// The parser will attempt to handle simple variable substitution, +/// respect single- vs. double-quotes, and ignore `#comments` or the `export` keyword. /// Loads environment variables from a `.env` file. /// /// ## usage @@ -19,4 +28,82 @@ /// /// const _requiredEnvVars = ['host', 'port']; /// bool get hasEnv => env.isEveryDefined(_requiredEnvVars); -export 'package:dotenv/src/dotenv.dart'; +class DotEnv { + /// If true, the underlying map will contain the entries of [Platform.environment], + /// even after calling [clear]. + /// Otherwise, it will be empty until populated by [load]. + final bool includePlatformEnvironment; + + /// If true, suppress "file not found" messages on [stderr] during [load]. + final bool quiet; + + final _map = {}; + + DotEnv({this.includePlatformEnvironment = false, this.quiet = false}) { + if (includePlatformEnvironment) _addPlatformEnvironment(); + } + + /// Provides access to the underlying [Map]. + /// + /// Prefer using [operator[]] to read values. + @visibleForTesting + Map get map => _map; + + /// Reads the value for [key] from the underlying map. + /// + /// Returns `null` if [key] is absent. See [isDefined]. + String? operator [](String key) => _map[key]; + + /// Calls [Map.addAll] on the underlying map. + void addAll(Map other) => _map.addAll(other); + + /// Calls [Map.clear] on the underlying map. + /// + /// If [includePlatformEnvironment] is true, the entries of [Platform.environment] will be reinserted. + void clear() { + _map.clear(); + if (includePlatformEnvironment) _addPlatformEnvironment(); + } + + /// Equivalent to [operator []] when the underlying map contains [key], + /// and the value is non-empty. See [isDefined]. + /// + /// Otherwise, calls [orElse] and returns the value. + String getOrElse(String key, String Function() orElse) => + isDefined(key) ? _map[key]! : orElse(); + + /// True if [key] has a nonempty value in the underlying map. + /// + /// Differs from [Map.containsKey] by excluding empty values. + bool isDefined(String key) => _map[key]?.isNotEmpty ?? false; + + /// True if all supplied [vars] have nonempty value; false otherwise. + /// + /// See [isDefined]. + bool isEveryDefined(Iterable vars) => vars.every(isDefined); + + /// Parses environment variables from each path in [filenames], and adds them + /// to the underlying [Map]. + /// + /// Logs to [stderr] if any file does not exist; see [quiet]. + void load([ + Iterable filenames = const ['.env'], + Parser psr = const Parser(), + ]) { + for (var filename in filenames) { + var f = File.fromUri(Uri.file(filename)); + var lines = _verify(f); + _map.addAll(psr.parse(lines)); + } + } + + void _addPlatformEnvironment() => _map.addAll(Platform.environment); + + List _verify(File f) { + if (!f.existsSync()) { + if (!quiet) stderr.writeln('[dotenv] Load failed: file not found: $f'); + return []; + } + return f.readAsLinesSync(); + } +} diff --git a/lib/src/parser.dart b/lib/parser.dart similarity index 93% rename from lib/src/parser.dart rename to lib/parser.dart index 21d4930..09d50fc 100644 --- a/lib/src/parser.dart +++ b/lib/parser.dart @@ -1,11 +1,10 @@ -import 'dart:io'; - import 'package:meta/meta.dart'; +import 'package:universal_io/io.dart'; /// Creates key-value pairs from strings formatted as environment /// variable definitions. class Parser { - static const _singleQuot = "'"; + static const _singleQuote = "'"; static const _keyword = 'export'; static final _comment = new RegExp(r'''#.*(?:[^'"])$'''); @@ -49,10 +48,10 @@ class Parser { if (k.isEmpty) return {}; var rhs = stripped.substring(idx + 1, stripped.length).trim(); - var quotChar = surroundingQuote(rhs); + var quoteChar = surroundingQuote(rhs); var v = unquote(rhs); - if (quotChar == _singleQuot) // skip substitution in single-quoted values + if (quoteChar == _singleQuote) // skip substitution in single-quoted values return {k: v}; return {k: interpolate(v, env)}; diff --git a/lib/src/dotenv.dart b/lib/src/dotenv.dart deleted file mode 100644 index bfac82e..0000000 --- a/lib/src/dotenv.dart +++ /dev/null @@ -1,90 +0,0 @@ -library dotenv; - -import 'dart:io'; - -import 'package:meta/meta.dart'; - -import 'parser.dart'; - -/// Loads key-value pairs from a file into a [Map]. -/// -/// The parser will attempt to handle simple variable substitution, -/// respect single- vs. double-quotes, and ignore `#comments` or the `export` keyword. -class DotEnv { - /// If true, the underlying map will contain the entries of [Platform.environment], - /// even after calling [clear]. - /// Otherwise, it will be empty until populated by [load]. - final bool includePlatformEnvironment; - - /// If true, suppress "file not found" messages on [stderr] during [load]. - final bool quiet; - - final _map = {}; - - DotEnv({this.includePlatformEnvironment = false, this.quiet = false}) { - if (includePlatformEnvironment) _addPlatformEnvironment(); - } - - /// Provides access to the underlying [Map]. - /// - /// Prefer using [operator[]] to read values. - @visibleForTesting - Map get map => _map; - - /// Reads the value for [key] from the underlying map. - /// - /// Returns `null` if [key] is absent. See [isDefined]. - String? operator [](String key) => _map[key]; - - /// Calls [Map.addAll] on the underlying map. - void addAll(Map other) => _map.addAll(other); - - /// Calls [Map.clear] on the underlying map. - /// - /// If [includePlatformEnvironment] is true, the entries of [Platform.environment] will be reinserted. - void clear() { - _map.clear(); - if (includePlatformEnvironment) _addPlatformEnvironment(); - } - - /// Equivalent to [operator []] when the underlying map contains [key], - /// and the value is non-empty. See [isDefined]. - /// - /// Otherwise, calls [orElse] and returns the value. - String getOrElse(String key, String Function() orElse) => - isDefined(key) ? _map[key]! : orElse(); - - /// True if [key] has a nonempty value in the underlying map. - /// - /// Differs from [Map.containsKey] by excluding empty values. - bool isDefined(String key) => _map[key]?.isNotEmpty ?? false; - - /// True if all supplied [vars] have nonempty value; false otherwise. - /// - /// See [isDefined]. - bool isEveryDefined(Iterable vars) => vars.every(isDefined); - - /// Parses environment variables from each path in [filenames], and adds them - /// to the underlying [Map]. - /// - /// Logs to [stderr] if any file does not exist; see [quiet]. - void load( - [Iterable filenames = const ['.env'], - Parser psr = const Parser()]) { - for (var filename in filenames) { - var f = File.fromUri(Uri.file(filename)); - var lines = _verify(f); - _map.addAll(psr.parse(lines)); - } - } - - void _addPlatformEnvironment() => _map.addAll(Platform.environment); - - List _verify(File f) { - if (!f.existsSync()) { - if (!quiet) stderr.writeln('[dotenv] Load failed: file not found: $f'); - return []; - } - return f.readAsLinesSync(); - } -} diff --git a/pubspec.yaml b/pubspec.yaml index 98a7e24..68f059d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dotenv -version: 4.2.0 +version: 4.3.0 description: Load environment variables from a `.env` file. homepage: https://github.com/mockturtl/dotenv @@ -9,6 +9,7 @@ environment: dependencies: args: ^2.3.0 meta: ^1.7.0 + universal_io: ^2.2.2 dev_dependencies: tidy: ^3.1.0 diff --git a/test/dotenv_test.dart b/test/dotenv_test.dart index b094518..339f88d 100644 --- a/test/dotenv_test.dart +++ b/test/dotenv_test.dart @@ -1,5 +1,5 @@ import 'package:collection/collection.dart' show MapEquality; -import 'package:dotenv/src/dotenv.dart'; +import 'package:dotenv/dotenv.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/parser_test.dart b/test/parser_test.dart index 79e3677..e2541d6 100644 --- a/test/parser_test.dart +++ b/test/parser_test.dart @@ -1,7 +1,7 @@ import 'dart:io'; import 'dart:math'; -import 'package:dotenv/src/parser.dart'; +import 'package:dotenv/parser.dart'; import 'package:test/test.dart'; void main() {