flutter compatibility 2.0.0

This commit is contained in:
James Collins
2018-12-12 12:50:32 +13:00
parent bc35b01fa0
commit 55eae6e3c1
30 changed files with 616 additions and 586 deletions

79
.gitignore vendored
View File

@ -1,10 +1,73 @@
.buildlog
.pub/
build/
packages
######################
# Project Specific
######################
*.g.dart
doc/
.env*
!example/.env.example
.dart_tool/
pubspec.lock
######################
# Intellij
######################
.idea*
*.iml
*.iws
*.ipr
*.ids
*.orig
classes/
build/
gen/
######################
# Visual Studio Code
######################
.vscode/
######################
# Windows
######################
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
######################
# Mac OSX
######################
.DS_Store
.svn
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
######################
# Flutter & Dart
######################
.packages
.pub/
.dart_tool/
.flutter-plugins
######################
# Directories
######################
/bin/
/deploy/
/out/
######################
# Logs
######################
*.log*
######################
# Others
######################
*.class
*.*~
*~
.merge_file*

View File

@ -1,6 +0,0 @@
language: dart
dart:
- stable
- dev
script: ./tool/travis.sh
sudo: false

View File

@ -7,14 +7,18 @@ Release notes are available on [github][notes].
[pub-semver]: https://www.dartlang.org/tools/pub/versioning.html#semantic-versions
[pub-semver-readme]: https://pub.dartlang.org/packages/pub_semver
[notes]: https://github.com/mockturtl/dotenv/releases
[notes]: https://github.com/java-james/flutter_dotenv/releases
2.0.0
-----
- Flutter compatible
1.0.0
-----
- Dart 2 compatible. [#16][]
#### 0.1.3+3
- [docs] tweak README
@ -65,12 +69,3 @@ Release notes are available on [github][notes].
-----
Initial release.
[#3]: https://github.com/mockturtl/dotenv/issues/3
[#5]: https://github.com/mockturtl/dotenv/issues/5
[#6]: https://github.com/mockturtl/dotenv/issues/6
[#7]: https://github.com/mockturtl/dotenv/issues/7
[#8]: https://github.com/mockturtl/dotenv/issues/8
[#10]: https://github.com/mockturtl/dotenv/issues/10
[#11]: https://github.com/mockturtl/dotenv/issues/11
[#16]: https://github.com/mockturtl/dotenv/issues/16

View File

@ -10,5 +10,5 @@ contributing
- Use [well-formatted commit messages][git-log-fmt].
[gh-issues]: https://github.com/mockturtl/dotenv/issues
[gh-issues]: https://github.com/java-james/flutter_dotenv/issues
[git-log-fmt]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015 mockturtl
Copyright (c) 2018 java-james
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,72 +1,57 @@
dotenv
======
flutter_dotenv
==============
Load environment variables at runtime from a `.env` file.
[![Pub Version][pub-badge]][pub]
[![Build Status][ci-badge]][ci]
[![Documentation][dartdocs-badge]][dartdocs]
[ci-badge]: https://travis-ci.org/mockturtl/dotenv.svg?branch=master
[ci]: https://travis-ci.org/mockturtl/dotenv
[pub-badge]: https://img.shields.io/pub/v/dotenv.svg
[pub]: https://pub.dartlang.org/packages/dotenv
[dartdocs-badge]: https://img.shields.io/badge/dartdocs-reference-blue.svg
[dartdocs]: http://www.dartdocs.org/documentation/dotenv/latest
Load configuration at runtime from a `.env` file which can be used throughout the applicaiton.
### about
Deploying applications should be simple. This implies constraints:
This library is a fork of [mockturtl/dotenv] dart library with slight changes to make this work with flutter.
It parses the `.env` file into a map contained within a singleton which allows the variables to be used throughout your application.
> **The [twelve-factor app][12fa] stores [config][cfg] in _environment variables_**
> (often shortened to _env vars_ or _env_). Env vars are easy to change
> between deploys without changing any code... they are a language- and
> OS-agnostic standard.
[12fa]: http://www.12factor.net
[cfg]: http://12factor.net/config
An _environment_ is the set of variables known to a process (say, `PATH`, `PORT`, ...).
It is desirable to mimic the production environment during development (testing,
staging, ...) by reading these values from a file.
This library parses that file and merges its values with the built-in
[`Platform.environment`][docs-io] map.
[docs-io]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:io.Platform#id_environment
[mockturtl/dotenv]: https://pub.dartlang.org/packages/dotenv
### usage
See [documentation][usage] and [examples][].
[usage]: http://www.dartdocs.org/documentation/dotenv/latest/index.html#dotenv/dotenv
[examples]: https://github.com/mockturtl/dotenv/tree/master/example
### cli
Get the latest:
Create a `.env` file in the root of your project with the example content:
```sh
$ pub global activate dotenv
VAR_NAME=HELLOWORLD
```
Run:
Add the `.env` file to your assets bundle in `pubspec.yaml`
```sh
$ pub global run dotenv:new # create a .env file and add it to .gitignore
$ pub global run dotenv # load the file and print the environment to stdout
```
assets:
- .env
```
Init the DotEnv singleton in `main.dart`
```
Future main() async {
await DotEnv().load('.env');
//...runapp
}
```
Access variables from `.env` throughout the applicaiton
```
DotEnv().env['VAR_NAME'];
```
Optionally you could map `DotEnv().env` after load to a config model to access config with types.
#### discussion
Use the [issue tracker][tracker] for bug reports and feature requests.
Pull requests gleefully considered.
Pull requests are welcome.
[tracker]: https://github.com/mockturtl/dotenv/issues
[tracker]: https://github.com/java-james/flutter_dotenv/issues
###### prior art
[flutter_dotenv]: https://pub.dartlang.org/packages/dotenv
- [mockturtl/dotenv][] (dart)
- [bkeepers/dotenv][] (ruby)
- [motdotla/dotenv][] (node)
- [theskumar/python-dotenv][] (python)
@ -77,6 +62,7 @@ Pull requests gleefully considered.
- [mefellows/sbt-dotenv][] (scala)
- [greenspun/dotenv][] (half of common lisp)
[mockturtl/dotenv]: https://pub.dartlang.org/packages/dotenv
[bkeepers/dotenv]: https://github.com/bkeepers/dotenv
[motdotla/dotenv]: https://github.com/motdotla/dotenv
[theskumar/python-dotenv]: https://github.com/theskumar/python-dotenv

View File

@ -1,3 +1,178 @@
# ~ Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
# ~ for details. All rights reserved. Use of this source code is governed by a
# ~ BSD-style license that can be found in the LICENSE file.
# ~
# ~ A living list of rules following the Effective Dart guide.
# ~ https://www.dartlang.org/guides/language/effective-dart
# ~
# ~ Note: not everything suggested in the guide has a rule associated with it.
analyzer:
strong-mode:
implicit-casts: false
#strong-mode:
#implicit-casts: false
#implicit-dynamic: false
errors:
todo: ignore
#exclude:
# ~ Effective dart lint rules form https://github.com/dart-lang/linter
# ~ We have implemented all but lines_longer_than_80_chars & public_member_api_docs
linter:
rules:
# ~ --- STYLE
# ~ identifiers
- camel_case_types
- library_names
- file_names
- library_prefixes
- non_constant_identifier_names
- constant_identifier_names # ~ prefer
# ~ ordering
- directives_ordering
# ~ formating
- curly_braces_in_flow_control_structures
# ~ --- DOCUMENTATION
# ~ comments
# ~ doc comments
- slash_for_doc_comments
- package_api_docs # ~ prefer
- comment_references
# ~ markdown
# ~ writing
# ~ --- USAGE
# ~ libraries
- implementation_imports
- avoid_relative_lib_imports
# ~ strings
- prefer_adjacent_string_concatenation
- prefer_interpolation_to_compose_strings # ~ prefer
- unnecessary_brace_in_string_interps # ~ avoid
# ~ collections
- prefer_collection_literals
- avoid_function_literals_in_foreach_calls # ~ avoid
- prefer_iterable_whereType
# ~ functions
- prefer_function_declarations_over_variables
- unnecessary_lambdas
# ~ parameters
- prefer_equal_for_default_values
# ~ variables
- avoid_init_to_null
# ~ members
- unnecessary_getters_setters
- prefer_final_fields
- prefer_expression_function_bodies # consider
- unnecessary_this
- prefer_typing_uninitialized_variables
# ~ constructors
- prefer_initializing_formals
- type_init_formals
- empty_constructor_bodies
- unnecessary_new
- unnecessary_const
# ~ error handling
- avoid_catches_without_on_clauses # ~ avoid
- use_rethrow_when_possible
# ~ asynchrony
# ~ --- DESIGN
# ~ names
- use_to_and_as_if_applicable
# ~ libraries
# ~ classes
- one_member_abstracts # ~ avoid
- avoid_classes_with_only_static_members # ~ avoid
# ~ constructors
- prefer_constructors_over_static_methods
# ~ members
- use_setters_to_change_properties
- avoid_setters_without_getters
- avoid_returning_null # ~ avoid
- avoid_returning_this # ~ avoid
# ~ types
- type_annotate_public_apis # ~ prefer
- omit_local_variable_types # ~ avoid
- avoid_types_on_closure_parameters # ~ avoid
- avoid_return_types_on_setters
- prefer_generic_function_type_aliases
- avoid_private_typedef_functions # ~ prefer
# ~ parameters
- avoid_positional_boolean_parameters # ~ avoid
# ~ equality
- hash_and_equals
- avoid_null_checks_in_equality_operators
# ~ Rules that are not used but are recommended by effective dart:
#
# - public_member_api_docs # ~ We don't have time to document everything yet
# - lines_longer_than_80_chars # prefer # ~ Doesn't seem reasonable
# ~ Rules that are not used
# - always_declare_return_types
# - always_put_control_body_on_new_line
# - always_put_required_named_parameters_first
# - always_require_non_null_named_parameters
# - always_specify_types
# - annotate_overrides
# - avoid_annotating_with_dynamic
# - avoid_as
# - avoid_bool_literals_in_conditional_expressions
# - avoid_catching_errors
# - avoid_double_and_int_checks
# - avoid_empty_else
# - avoid_field_initializers_in_const_classes
# - avoid_js_rounded_ints
# - avoid_renaming_method_parameters
# - avoid_single_cascade_in_expression_statements
# - avoid_slow_async_io
# - avoid_types_as_parameter_names
# - avoid_unused_constructor_parameters
# - await_only_futures
# - cancel_subscriptions
# - cascade_invocations
# - close_sinks
# - control_flow_in_finally
# - empty_catches
# - empty_statements
# - invariant_booleans
# - iterable_contains_unrelated_type
# - join_return_with_assignment
# - list_remove_unrelated_type
# - literal_only_boolean_expressions
# - no_adjacent_strings_in_list
# - no_duplicate_case_values
# - null_closures
# - only_throw_errors
# - overridden_fields
# - package_names
# - package_prefixed_library_names
# - parameter_assignments
# - prefer_asserts_in_initializer_lists
# - prefer_bool_in_asserts
# - prefer_conditional_assignment
# - prefer_const_constructors
# - prefer_const_constructors_in_immutables
# - prefer_const_declarations
# - prefer_const_literals_to_create_immutables
# - prefer_contains
# - prefer_expression_function_bodies
# - prefer_final_locals
# - prefer_foreach
# - prefer_is_empty
# - prefer_is_not_empty
# - prefer_single_quotes
# - recursive_getters
# - sort_constructors_first
# - sort_unnamed_constructors_first
# - super_goes_last
# - test_types_in_equals
# - throw_in_finally
# - unawaited_futures
# - unnecessary_null_aware_assignments
# - unnecessary_null_in_if_null_operators
# - unnecessary_overrides
# - unnecessary_parenthesis
# - unnecessary_statements
# - unrelated_type_equality_checks
# - use_string_buffers
# - valid_regexps
# - void_checks

View File

@ -0,0 +1,23 @@
package io.flutter.plugins;
import io.flutter.plugin.common.PluginRegistry;
/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}

3
android/local.properties Normal file
View File

@ -0,0 +1,3 @@
sdk.dir=/Users/jamescollins/Library/Android/sdk
flutter.sdk=/Users/jamescollins/Development/sdks/flutter
flutter.versionName=2.0.0

View File

@ -1,33 +0,0 @@
import 'dart:io';
import 'package:args/args.dart';
import 'package:dotenv/dotenv.dart' as dotenv;
final _argPsr = new ArgParser()
..addFlag('help', abbr: 'h', negatable: false, help: 'Print this help text.')
..addOption('file',
abbr: 'f',
defaultsTo: '.env',
help:
'File to read.\nProvides environment variable definitions, one per line.');
/// Prints the [env] map.
///
/// ## usage
///
/// pub global run dotenv --help
void main(List<String> argv) {
var opts = _argPsr.parse(argv);
if (opts['help'] == true) return _usage();
dotenv.load(opts['file'] as String);
_p(dotenv.env);
}
void _usage() {
_p('Parse variable definitions from a file, print the environment and exit.');
_p('Usage: pub global run dotenv [-f <file>]\n${_argPsr.usage}');
}
void _p(msg) => stdout.writeln(msg);

View File

@ -1,61 +0,0 @@
import 'dart:io' hide FileMode;
import 'package:args/args.dart';
import 'package:dart2_constant/io.dart';
const String gitignore = '.gitignore';
final _argPsr = new ArgParser()
..addFlag('help', abbr: 'h', negatable: false, help: 'Print this help text.')
..addOption('file',
abbr: 'f',
defaultsTo: '.env',
help: 'File to create.\nDo not check secrets into version control!');
/// Creates `.env` if the file does not exist, and adds it to `.gitignore`.
///
/// ## usage
///
/// pub global run dotenv:new --help
void main(List<String> args) {
var opts = _argPsr.parse(args);
if (opts['help'] == true) return _usage();
String envFile = opts['file'].toString();
_touch(envFile);
var g = _touch(gitignore);
if (_anyLineContains(envFile, g)) return _handleIgnored(envFile);
_appendTo(g, '$envFile*');
}
bool _anyLineContains(String str, File f) =>
f.readAsLinesSync().any((line) => line.contains(str));
void _handleIgnored(String filename) {
_pErr("Found $gitignore with line containing '$filename'; exiting.");
exitCode = 1;
}
File _touch(String filename) {
var f = new File.fromUri(new Uri.file(filename));
if (f.existsSync()) return f;
f.createSync();
_p('Created file: $filename');
return f;
}
void _appendTo(File f, String line) {
f.writeAsStringSync('$line\n', mode: FileMode.append);
_p("Added '$line' to ${f.path}.");
}
void _usage() {
_p('Create a new file and gitignore it.');
_p('Usage: pub global run dotenv:new [-f <file>]\n${_argPsr.usage}');
}
void _p(String msg) => stdout.writeln(msg);
void _pErr(String msg) => stderr.writeln(msg);

View File

@ -1,2 +0,0 @@
foo = 'bar' # comments and whitespace will be stripped
export baz="qux"

View File

@ -1,21 +0,0 @@
example
=======
Note consuming code must call `load` before accessing the `env` map.
usage
-----
From this directory, run
```sh
$ dart example.dart
```
###### setup
Define variables in a `.env` file.
```sh
$ cp .env.example .env
```

View File

@ -1,22 +0,0 @@
import 'dart:io';
import 'package:dotenv/dotenv.dart' show load, clean, isEveryDefined, env;
void main() {
load();
p('read all vars? ${isEveryDefined(['foo', 'baz'])}');
p('value of foo is ${env['foo']}');
p('value of baz is ${env['baz']}');
p('your home directory is: ${env['HOME']}');
clean();
p('cleaned!');
p('env has key foo? ${env.containsKey('foo')}');
p('env has key baz? ${env.containsKey('baz')}');
p('your home directory is still: ${env['HOME']}');
}
p(String msg) => stdout.writeln(msg);

19
flutter-dotenv.iml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View File

@ -0,0 +1,8 @@
// This is a generated file; do not edit or check into version control.
FLUTTER_ROOT=/Users/jamescollins/Development/sdks/flutter
FLUTTER_APPLICATION_PATH=/Users/jamescollins/Development/spark/flutter-dotenv
FLUTTER_TARGET=lib/main.dart
FLUTTER_BUILD_DIR=build
SYMROOT=${SOURCE_ROOT}/../build/ios
FLUTTER_FRAMEWORK_DIR=/Users/jamescollins/Development/sdks/flutter/bin/cache/artifacts/engine/ios
FLUTTER_BUILD_NAME=0.1.0

View File

@ -0,0 +1,14 @@
//
// Generated file. Do not edit.
//
#ifndef GeneratedPluginRegistrant_h
#define GeneratedPluginRegistrant_h
#import <Flutter/Flutter.h>
@interface GeneratedPluginRegistrant : NSObject
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
@end
#endif /* GeneratedPluginRegistrant_h */

View File

@ -0,0 +1,12 @@
//
// Generated file. Do not edit.
//
#import "GeneratedPluginRegistrant.h"
@implementation GeneratedPluginRegistrant
+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
}
@end

View File

@ -1,52 +0,0 @@
/// Loads environment variables from a `.env` file.
///
/// ## usage
///
/// Once you call [load], the top-level [env] map is available.
/// You may wish to prefix the import.
///
/// import 'package:dotenv/dotenv.dart' show load, env;
///
/// void main() {
/// load();
/// var x = env['foo'];
/// // ...
/// }
///
/// Verify required variables are present:
///
/// const _requiredEnvVars = const ['host', 'port'];
/// bool get hasEnv => isEveryDefined(_requiredEnvVars);
library dotenv;
import 'dart:io';
import 'package:meta/meta.dart';
part 'src/parser.dart';
var _env = new Map<String, String>.from(Platform.environment);
/// A copy of [Platform.environment](dart:io) including variables loaded at runtime from a file.
Map<String, String> get env => _env;
/// Overwrite [env] with a new writable copy of [Platform.environment](dart:io).
Map clean() => _env = new Map.from(Platform.environment);
/// True if all supplied variables have nonempty value; false otherwise.
/// Differs from [containsKey](dart:core) by excluding null values.
/// Note [load] should be called first.
bool isEveryDefined(Iterable<String> vars) =>
vars.every((k) => _env[k] != null && _env[k].isNotEmpty);
/// Read environment variables from [filename] and add them to [env].
/// Logs to [stderr] if [filename] does not exist.
void load([String filename = '.env', Parser psr = const Parser()]) {
var f = new File.fromUri(new Uri.file(filename));
var lines = _verify(f);
_env.addAll(psr.parse(lines));
}
List<String> _verify(File f) {
if (f.existsSync()) return f.readAsLinesSync();
stderr.writeln('[dotenv] Load failed: file not found: $f');
return [];
}

4
lib/flutter_dotenv.dart Normal file
View File

@ -0,0 +1,4 @@
library flutterdotenv;
export 'src/dotenv.dart';
export 'src/parser.dart';

92
lib/src/dotenv.dart Normal file
View File

@ -0,0 +1,92 @@
/// Loads environment variables from a `.env` file.
///
/// ## usage
///
/// Once you call [load] or the factory constructor with a valid env, the top-level [env] map is available.
/// You may wish to prefix the import.
///
/// import 'package:flutter_dotenv/flutter_dotenv.dart' show load, env;
///
/// void main() {
/// await DotEnv().load('.env');
/// runApp(App());
/// var x = DotEnv().env['foo'];
/// // ...
/// }
///
/// Verify required variables are present:
///
/// const _requiredEnvVars = const ['host', 'port'];
/// bool get hasEnv => isEveryDefined(_requiredEnvVars);
import 'dart:async';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import './parser.dart';
///
/// ## usage
///
/// Future main() async {
/// await DotEnv().load('.env');
/// //...runapp
/// }
///
/// Verify required variables are present:
///
/// const _requiredEnvVars = const ['host', 'port'];
/// bool get hasEnv => isEveryDefined(_requiredEnvVars);
class DotEnv {
Map<String, String> _env = <String, String>{};
static DotEnv _singleton;
Map<String, String> get env {
if (_env.isEmpty) {
stderr.writeln(
'[flutter_dotenv] No env values found. Make sure you have called DotEnv.load()');
}
return _env;
}
set env(Map<String, String> env) {
_env = env;
}
/// True if all supplied variables have nonempty value; false otherwise.
/// Differs from [containsKey](dart:core) by excluding null values.
/// Note [load] should be called first.
bool isEveryDefined(Iterable<String> vars) =>
vars.every((k) => env[k] != null && env[k].isNotEmpty);
/// Read environment variables from [filename] and add them to [env].
/// Logs to [stderr] if [filename] does not exist.
Future load([String filename = '.env', Parser psr = const Parser()]) async {
var lines = await _verify(filename);
env.addAll(psr.parse(lines));
}
Future<List<String>> _verify(String filename) async {
try {
var str = await rootBundle.loadString(filename);
if (str.isNotEmpty) return str.split('\n');
stderr.writeln('[flutter_dotenv] Load failed: file $filename was empty');
} on FlutterError {
stderr.writeln('[flutter_dotenv] Load failed: file not found');
}
return [];
}
factory DotEnv({Map<String, String> env}) {
if (_singleton == null) {
_singleton = DotEnv._internal(env: env);
}
return _singleton;
}
DotEnv._internal({Map<String, String> env})
: _env = env ?? <String, String>{};
}

View File

@ -1,35 +1,32 @@
part of dotenv;
import 'package:meta/meta.dart';
/// Creates key-value pairs from strings formatted as environment
/// variable definitions.
class Parser {
static const _singleQuot = "'";
static const _keyword = 'export';
static final _comment = new RegExp(r'''#.*(?:[^'"])$''');
static final _surroundQuotes = new RegExp(r'''^(['"])(.*)\1$''');
static final _bashVar =
new RegExp(r'(?:\\)?(\$)(?:{)?([a-zA-Z_][\w]*)+(?:})?');
static final _comment = RegExp(r'''#.*(?:[^'"])$''');
static final _surroundQuotes = RegExp(r'''^(['"])(.*)\1$''');
/// [Parser] methods are pure functions.
const Parser();
/// Creates a [Map](dart:core) suitable for merging into [Platform.environment](dart:io).
/// Creates a [Map](dart:core)
/// Duplicate keys are silently discarded.
Map<String, String> parse(Iterable<String> lines) {
var out = <String, String>{};
lines.forEach((line) {
for (var line in lines) {
var kv = parseOne(line, env: out);
if (kv.isEmpty) return;
if (kv.isEmpty) continue;
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 {}}) {
{Map<String, String> env = const {}}) {
var stripped = strip(line);
if (!_isValid(stripped)) return {};
@ -39,24 +36,11 @@ class Parser {
if (k.isEmpty) return {};
var rhs = sides[1].trim();
var quotChar = surroundingQuote(rhs);
var v = unquote(rhs);
if (quotChar == _singleQuot) // skip substitution in single-quoted values
return {k: v};
return {k: interpolate(v, env)};
return {k: v};
}
/// 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);
if (!_has(env, k)) return _tryPlatformEnv(k);
return env[k];
});
/// If [val] is wrapped in single or double quotes, returns the quote character.
/// Otherwise, returns the empty string.
@visibleForTesting
@ -79,13 +63,4 @@ class Parser {
String swallow(String line) => line.replaceAll(_keyword, '').trim();
bool _isValid(String s) => s.isNotEmpty && s.contains('=');
/// [null] is a valid value in a Dart map, but the env var representation is empty string, not the string 'null'
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];
}
}

132
pubspec.lock Normal file
View File

@ -0,0 +1,132 @@
# Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.dartlang.org"
source: hosted
version: "1.14.11"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.3+1"
meta:
dependency: "direct main"
description:
name: meta
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
path:
dependency: transitive
description:
name: path
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.2"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.dartlang.org"
source: hosted
version: "1.4.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.dartlang.org"
source: hosted
version: "1.9.3"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.dartlang.org"
source: hosted
version: "1.6.8"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.1"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.6"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
sdks:
dart: ">=2.0.0 <3.0.0"

View File

@ -1,14 +1,14 @@
name: dotenv
version: 1.0.0
description: Load environment variables from a `.env` file.
author: mockturtl <mockturtl@gmail.com>
homepage: https://github.com/mockturtl/dotenv
name: flutter_dotenv
version: 2.0.0
description: Load global app config from a `.env` file.
author: java-james <james-collins@hotmail.co.nz>
homepage: https://github.com/java-james/flutter_dotenv
environment:
sdk: '>=1.8.0 <3.0.0'
sdk: '>=2.0.0 <3.0.0'
dependencies:
args: ^1.0.0
dart2_constant: ^1.0.0
meta: ^1.0.2
flutter:
sdk: flutter
meta: 1.1.6
dev_dependencies:
test: any
collection: any
flutter_test:
sdk: flutter

View File

@ -1,54 +0,0 @@
import 'dart:io';
import 'package:collection/collection.dart' show MapEquality;
import 'package:dotenv/dotenv.dart' as dotenv;
import 'package:test/test.dart';
void main() {
group('[dotenv]', () {
var subj = new DotenvTest();
setUp(() => dotenv.env.addAll(vars));
tearDown(() => dotenv.clean());
test('it can clean previously defined variables', subj.clean);
test('it is equal to the read-only process environment when clean',
subj.clean2);
test('it confirms all required vars are defined', subj.every);
test('it fails when a required var is not defined', subj.every_fail);
test('it loads the file', subj.load, skip: 'pending');
});
}
const extra = const {'servlets': 'yes', 'rats': 'yes', 'horses': 'omgyes'};
const vars = const {'x': '1', 'y': 'false', 'z': 'foo', 'empty': ''};
class DotenvTest {
void clean() {
dotenv.env.addAll(extra);
dotenv.clean();
extra.keys.forEach((k) => expect(dotenv.env.containsKey(k), isFalse));
vars.keys.forEach((k) => expect(dotenv.env.containsKey(k), isFalse));
}
void clean2() {
expect(_clean, isFalse);
dotenv.clean();
expect(_clean, isTrue);
}
void every() {
dotenv.env.addAll(extra);
expect(dotenv.isEveryDefined(['x', 'y', 'z']), isTrue);
expect(dotenv.isEveryDefined(['servlets', 'rats', 'horses']), isTrue);
}
void every_fail() {
expect(dotenv.isEveryDefined(['empty']), isFalse);
expect(dotenv.isEveryDefined(['no_such_key']), isFalse);
}
void load() {}
}
bool get _clean => const MapEquality().equals(dotenv.env, Platform.environment);

View File

@ -1,203 +0,0 @@
import 'dart:io';
import 'dart:math';
import 'package:dotenv/dotenv.dart';
import 'package:test/test.dart';
const ceil = 100000;
Random rand;
void main() {
group('[Parser]', () {
setUp(() => rand = new Random());
var subj = new ParserTest();
test('it swallows "export"', subj.swallow);
test('it strips trailing comments', subj.strip);
test('it ignores comment lines', subj.strip_line);
test('it handles unquoted values', subj.unquote_noop);
test('it handles double quoted values', subj.unquote_double);
test('it handles single quoted values', subj.unquote_single);
test('it handles escaped quotes within values', subj.unquote_escape);
test('it skips empty lines', subj.parse_empty);
test('it ignores duplicate keys', subj.parse_dup);
test('it substitutes known variables into other values', subj.parse_subs);
test('it discards surrounding quotes', subj.parse_quot);
test('it detects unquoted values', subj.surroundingQuote_none);
test('it detects double-quoted values', subj.surroundingQuote_double);
test('it detects single-quoted values', subj.surroundingQuote_single);
test('it performs variable substitution', subj.interpolate);
test('it skips undefined variables', subj.interpolate_missing);
test('it handles explicitly null values in env', subj.interpolate_missing2);
test('it falls back to the process environment for undefined variables',
subj.interpolate_fallback);
test('it handles \${surrounding braces} on vars', subj.interpolate_curlies);
test('it knows quoted # is not a comment', subj.parseOne_pound);
test('it handles quotes in a comment',
subj.parseOne_commentQuote_terminalChar);
test('it does NOT handle comments ending with a quote',
subj.parseOne_commentQuote_terminalChar2);
test('it skips var substitution in single quotes', subj.parseOne_quot);
test('it performs var subs in double quotes', subj.parseOne_doubleQuot);
test('it performs var subs without quotes', subj.parseOne_unQuot);
});
}
const _psr = const Parser();
class ParserTest {
void parseOne_commentQuote_terminalChar2() {
var fail =
_psr.parseOne('fruit = banana # I\'m a comment with a final "quote"');
expect(
fail['fruit'], equals('banana # I\'m a comment with a final "quote"'));
}
void parseOne_commentQuote_terminalChar() {
// note terminal whitespace
var sing = _psr.parseOne("fruit = 'banana' # comments can be 'sneaky!' ");
var doub = _psr.parseOne('fruit = "banana" # comments can be "sneaky!" ');
var none = _psr.parseOne('fruit = banana # comments can be "sneaky!" ');
expect(sing['fruit'], equals('banana'));
expect(doub['fruit'], equals('banana'));
expect(none['fruit'], equals('banana'));
}
void parseOne_pound() {
var double = _psr.parseOne('foo = "ab#c"');
var single = _psr.parseOne("foo = 'ab#c'");
expect(double['foo'], equals('ab#c'));
expect(single['foo'], equals('ab#c'));
}
void interpolate() {
var out = _psr.interpolate(r'a$foo$baz', {'foo': 'bar', 'baz': 'qux'});
expect(out, equals('abarqux'));
}
void interpolate_missing() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.interpolate('a\$jinx_$r', {});
expect(out, equals('a'));
}
void interpolate_missing2() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.interpolate('a\$foo_$r\$baz_$r', {'foo_$r': null});
expect(out, equals('a'));
}
void interpolate_fallback() {
var out = _psr.interpolate('a\$HOME', {});
expect(out, equals('a${Platform.environment['HOME']}'));
}
void interpolate_curlies() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.interpolate('optional_\${foo_$r}', {'foo_$r': 'curlies'});
expect(out, equals('optional_curlies'));
}
void parseOne_quot() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.parseOne("some_var='my\$key_$r'", env: {'key_$r': 'val'});
expect(out['some_var'], equals('my\$key_$r'));
}
void parseOne_doubleQuot() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.parseOne('some_var="my\$key_$r"', env: {'key_$r': 'val'});
expect(out['some_var'], equals('myval'));
}
void parseOne_unQuot() {
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
var out = _psr.parseOne("some_var=my\$key_$r", env: {'key_$r': 'val'});
expect(out['some_var'], equals('myval'));
}
void surroundingQuote_none() {
var out = _psr.surroundingQuote('no quotes here!');
expect(out, isEmpty);
}
void surroundingQuote_single() {
var out = _psr.surroundingQuote("'single quoted'");
expect(out, equals("'"));
}
void surroundingQuote_double() {
var out = _psr.surroundingQuote('"double quoted"');
expect(out, equals('"'));
}
void swallow() {
var out = _psr.swallow(' export foo = bar ');
expect(out, equals('foo = bar'));
}
void strip() {
var out = _psr.strip(
'needs=explanation # It was the year when they finally immanentized the Eschaton.');
expect(out, equals('needs=explanation'));
}
void strip_line() {
var out =
_psr.strip(' # It was the best of times, it was a waste of time.');
expect(out, isEmpty);
}
void unquote_single() {
var out = _psr.unquote("'val'");
expect(out, equals('val'));
}
void unquote_noop() {
var out = _psr.unquote('str');
expect(out, equals('str'));
}
void unquote_double() {
var out = _psr.unquote('"val"');
expect(out, equals('val'));
}
void unquote_escape() {
var out = _psr.unquote("val_with_\"escaped\"_\'quote\'s");
expect(out, equals('''val_with_"escaped"_'quote's'''));
}
void parse_empty() {
var out = _psr.parse([
'# Define environment variables.',
' # comments will be stripped',
'foo=bar # trailing junk',
' baz = qux',
'# another comment'
]);
expect(out, equals({'foo': 'bar', 'baz': 'qux'}));
}
void parse_dup() {
var out = _psr.parse(['foo=bar', 'foo=baz']);
expect(out, equals({'foo': 'bar'}));
}
void parse_subs() {
var out = _psr.parse(['foo=bar', r'baz=super$foo']);
expect(out, equals({'foo': 'bar', 'baz': 'superbar'}));
}
void parse_quot() {
var out = _psr.parse([r"foo = 'bar'", r'export baz="qux"']);
expect(out, equals({'foo': 'bar', 'baz': 'qux'}));
}
}

View File

@ -17,10 +17,6 @@ Runs the official [code formatter][].
$ pub global activate dart_style
```
### [test.sh](test.sh)
Runs the unit test suite.
### [docs.sh](docs.sh)
Preview [dartdoc][] documentation.
@ -33,10 +29,6 @@ Preview [dartdoc][] documentation.
$ pub global activate dartdoc
```
### [travis.sh](travis.sh)
Run the analyzer and unit tests on Travis CI.
### [release.sh](release.sh)
`git tag` and `pub publish`.

View File

@ -2,4 +2,4 @@
# Autoformat code in-place, per style guidelines.
dartfmt -w bin lib test example
dartfmt -w ../lib

View File

@ -1,4 +0,0 @@
#!/bin/sh
j=$(nproc)
pub run test -j$j

View File

@ -1,5 +0,0 @@
#!/bin/bash -e
dartanalyzer --fatal-warnings {bin,lib,example}/*.dart
pub run test