mirror of
https://github.com/java-james/flutter_dotenv.git
synced 2025-07-04 13:27:55 +08:00
feat: migrate to null-safety (#24)
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,6 +8,7 @@
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
#*.env
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
@ -34,4 +35,3 @@ build/
|
||||
!example/ios/**/default.pbxuser
|
||||
!example/ios/**/default.perspectivev3
|
||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||
.env*
|
||||
|
13
CHANGELOG.md
13
CHANGELOG.md
@ -9,6 +9,19 @@ Release notes are available on [github][notes].
|
||||
[pub-semver-readme]: https://pub.dartlang.org/packages/pub_semver
|
||||
[notes]: https://github.com/java-james/flutter_dotenv/releases
|
||||
|
||||
4.0.0-nullsafety.0
|
||||
-----
|
||||
|
||||
- [BREAKING] Opt into null-safety
|
||||
- [deps] Upgrade dart sdk constraints to ```>=2.12.0-0 <3.0.0```
|
||||
- [new] Allow for escape of $ ' " and \n characters
|
||||
- [fix] Ensure swallow function only removes leading 'export' keyword
|
||||
- [fix] Retain spaces within single or double quotes
|
||||
- [fix] Allow for comments after matching end quotes
|
||||
- [new] Migrate to null safety
|
||||
- [new] Create unit test cases for parse
|
||||
|
||||
|
||||
#### 3.1.0
|
||||
|
||||
- [new] Allow merging with a custom map on load
|
||||
|
@ -52,7 +52,7 @@ Add the `.env` file to your assets bundle in `pubspec.yaml`
|
||||
Optionally add the `.env` file as an entry in your `.gitignore` if it isn't already
|
||||
|
||||
```sh
|
||||
.env*
|
||||
*.env
|
||||
```
|
||||
|
||||
Load the `.env` file in `main.dart`
|
||||
|
1
example/.gitignore
vendored
1
example/.gitignore
vendored
@ -250,3 +250,4 @@ GoogleService-Info.plist
|
||||
google-services.json
|
||||
# Plugins (already resolved through pubspec.yaml)
|
||||
.flutter-plugins
|
||||
*.env
|
||||
|
10
example/.metadata
Normal file
10
example/.metadata
Normal file
@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: 4b50ca7f7fbf56be72e54cd200825b760416a356
|
||||
channel: beta
|
||||
|
||||
project_type: app
|
@ -0,0 +1,6 @@
|
||||
package com.example.example
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity: FlutterActivity() {
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Modify this file to customize your launch splash screen -->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="?android:colorBackground" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
18
example/android/app/src/main/res/values-night/styles.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
Flutter draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
31
example/assets/.env
Normal file
31
example/assets/.env
Normal file
@ -0,0 +1,31 @@
|
||||
FOO=foo
|
||||
BAR=bar
|
||||
FOOBAR=\$FOO$BAR
|
||||
ESCAPED_DOLLAR_SIGN="\$1000"
|
||||
ESCAPED_QUOTE='\''
|
||||
|
||||
BASIC=basic
|
||||
|
||||
BASIC=basic1
|
||||
|
||||
# previous line intentionally left blank
|
||||
AFTER_LINE=after_line
|
||||
EMPTY=
|
||||
SINGLE_QUOTES='single_quotes'
|
||||
SINGLE_QUOTES_SPACED=' single quotes '
|
||||
DOUBLE_QUOTES="double_quotes"
|
||||
DOUBLE_QUOTES_SPACED=" double quotes "
|
||||
EXPAND_NEWLINES="expand\nnew\nlines"
|
||||
DONT_EXPAND_UNQUOTED=dontexpand\nnewlines
|
||||
DONT_EXPAND_SQUOTED='dontexpand\nnewlines'
|
||||
# COMMENTS=work
|
||||
EQUAL_SIGNS=equals==
|
||||
RETAIN_INNER_QUOTES={"foo": "bar"}
|
||||
RETAIN_LEADING_DQUOTE="retained
|
||||
RETAIN_LEADING_SQUOTE='retained
|
||||
RETAIN_TRAILING_DQUOTE=retained"
|
||||
RETAIN_TRAILING_SQUOTE=retained'
|
||||
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}'
|
||||
TRIM_SPACE_FROM_UNQUOTED= some spaced out string
|
||||
USERNAME=therealnerdybeast@example.tld
|
||||
SPACED_KEY = parsed
|
@ -1,70 +1,43 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart' as dotenv;
|
||||
|
||||
Future main() async {
|
||||
await DotEnv.load();
|
||||
await dotenv.load(mergeWith: {
|
||||
'TEST_VAR': '5',
|
||||
}); // mergeWith optional, you can include Platform.environment for Mobile/Desktop app
|
||||
|
||||
runApp(MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => MaterialApp(
|
||||
title: 'Flutter Demo',
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
),
|
||||
home: MyHomePage(title: 'Flutter Demo Home Page'),
|
||||
);
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
MyHomePage({Key key, this.title}) : super(key: key);
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
_MyHomePageState createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
print(DotEnv.env);
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.title),
|
||||
),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
'You have pushed the button this many times:}',
|
||||
title: 'Dotenv Demo',
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text('Dotenv Demo'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: FutureBuilder<String>(
|
||||
future: rootBundle.loadString('assets/.env'),
|
||||
initialData: '',
|
||||
builder: (context, snapshot) => Container(
|
||||
padding: EdgeInsets.all(50),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
'Env map: ${dotenv.env.toString()}',
|
||||
),
|
||||
Divider(thickness: 5),
|
||||
Text('Original'),
|
||||
Divider(),
|
||||
Text(snapshot.data ?? ''),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
),
|
||||
Text(
|
||||
'Env map: ${DotEnv.env}',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||
version: 1.0.0+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
sdk: ">=2.12.0-0 <3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
@ -40,37 +40,5 @@ dev_dependencies:
|
||||
|
||||
# The following section is specific to Flutter.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
|
||||
assets:
|
||||
- .env
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware.
|
||||
|
||||
# For details regarding adding assets from package dependencies, see
|
||||
# https://flutter.dev/assets-and-images/#from-packages
|
||||
|
||||
# To add custom fonts to your application, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
- assets/.env
|
||||
|
@ -1,30 +1 @@
|
||||
// This is a basic Flutter widget test.
|
||||
//
|
||||
// To perform an interaction with a widget in your test, use the WidgetTester
|
||||
// utility that Flutter provides. For example, you can send tap and scroll
|
||||
// gestures. You can also use WidgetTester to find child widgets in the widget
|
||||
// tree, read text, and verify that the values of widget properties are correct.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:flutter_dotenv_example/main.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
|
||||
// Build our app and trigger a frame.
|
||||
await tester.pumpWidget(MyApp());
|
||||
|
||||
// Verify that our counter starts at 0.
|
||||
expect(find.text('0'), findsOneWidget);
|
||||
expect(find.text('1'), findsNothing);
|
||||
|
||||
// Tap the '+' icon and trigger a frame.
|
||||
await tester.tap(find.byIcon(Icons.add));
|
||||
await tester.pump();
|
||||
|
||||
// Verify that our counter has incremented.
|
||||
expect(find.text('0'), findsNothing);
|
||||
expect(find.text('1'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
void main() {}
|
||||
|
BIN
example/web/favicon.png
Normal file
BIN
example/web/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 917 B |
BIN
example/web/icons/Icon-192.png
Normal file
BIN
example/web/icons/Icon-192.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
BIN
example/web/icons/Icon-512.png
Normal file
BIN
example/web/icons/Icon-512.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
45
example/web/index.html
Normal file
45
example/web/index.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!--
|
||||
If you are serving your web app in a path other than the root, change the
|
||||
href value below to reflect the base path you are serving from.
|
||||
|
||||
The path provided below has to start and end with a slash "/" in order for
|
||||
it to work correctly.
|
||||
|
||||
Fore more details:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
|
||||
-->
|
||||
<base href="/">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="A new Flutter project.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="example">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>example</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||
application. For more information, see:
|
||||
https://developers.google.com/web/fundamentals/primers/service-workers -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('flutter-first-frame', function () {
|
||||
navigator.serviceWorker.register('flutter_service_worker.js');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="main.dart.js" type="application/javascript"></script>
|
||||
</body>
|
||||
</html>
|
23
example/web/manifest.json
Normal file
23
example/web/manifest.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "example",
|
||||
"short_name": "example",
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"background_color": "#0175C2",
|
||||
"theme_color": "#0175C2",
|
||||
"description": "A new Flutter project.",
|
||||
"orientation": "portrait-primary",
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
@ -58,11 +58,25 @@ Future load(
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
Future testLoad(
|
||||
{String fileInput = '',
|
||||
Parser parser = const Parser(),
|
||||
Map<String, String> mergeWith = const {}}) async {
|
||||
clean();
|
||||
final linesFromFile = fileInput.split('\n');
|
||||
final linesFromMergeWith =
|
||||
mergeWith.entries.map((entry) => "${entry.key}=${entry.value}").toList();
|
||||
final allLines = linesFromMergeWith..addAll(linesFromFile);
|
||||
final envEntries = parser.parse(allLines);
|
||||
_envMap.addAll(envEntries);
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
/// 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) => _envMap[k] != null && _envMap[k].isNotEmpty);
|
||||
vars.every((k) => _envMap[k]?.isNotEmpty ?? false);
|
||||
|
||||
Future<List<String>> _getEntriesFromFile(String filename) async {
|
||||
try {
|
||||
|
@ -1,14 +1,13 @@
|
||||
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 = RegExp(r'''#.*(?:[^'"])$''');
|
||||
static final _surroundQuotes = RegExp(r'''^(['"])(.*)\1$''');
|
||||
static final _bashVar = RegExp(r'(?:\\)?(\$)(?:{)?([a-zA-Z_][\w]*)+(?:})?');
|
||||
static final _leadingExport = RegExp(r'''^ *export ?''');
|
||||
static final _comment = RegExp(r'''#[^'"]*$''');
|
||||
static final _commentWithQuotes = RegExp(r'''#.*$''');
|
||||
static final _surroundQuotes = RegExp(r'''^(["'])(.*?[^\\])\1''');
|
||||
static final _bashVar =
|
||||
RegExp(r'(?<=^|[^\\])(\$)(?:{)?([a-zA-Z_][\w]*)+(?:})?');
|
||||
|
||||
/// [Parser] methods are pure functions.
|
||||
const Parser();
|
||||
@ -17,18 +16,17 @@ class Parser {
|
||||
/// 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 {};
|
||||
|
||||
@ -40,48 +38,50 @@ class Parser {
|
||||
var rhs = stripped.substring(idx + 1, stripped.length).trim();
|
||||
var quotChar = surroundingQuote(rhs);
|
||||
var v = unquote(rhs);
|
||||
|
||||
if (quotChar == _singleQuot) {
|
||||
v = v.replaceAll("\\'", "'");
|
||||
return {k: v};
|
||||
}
|
||||
|
||||
final interpolatedValue = interpolate(v, env);
|
||||
if (quotChar == '"') {
|
||||
v = v.replaceAll('\\"', '"').replaceAll('\\n', '\n');
|
||||
}
|
||||
final interpolatedValue = interpolate(v, env).replaceAll("\\\$", "\$");
|
||||
return {k: interpolatedValue};
|
||||
}
|
||||
|
||||
/// Substitutes $bash_vars in [val] with values from [env].
|
||||
@visibleForTesting
|
||||
String interpolate(String val, Map<String, String> env) =>
|
||||
String interpolate(String val, Map<String, String?> env) =>
|
||||
val.replaceAllMapped(_bashVar, (m) {
|
||||
var k = m.group(2);
|
||||
var k = m.group(2)!;
|
||||
if (!_has(env, k)) return '';
|
||||
return env[k];
|
||||
return 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);
|
||||
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();
|
||||
String unquote(String val) {
|
||||
if (!_surroundQuotes.hasMatch(val))
|
||||
return strip(val, includeQuotes: true).trim();
|
||||
return _surroundQuotes.firstMatch(val)!.group(2)!;
|
||||
}
|
||||
|
||||
/// Strips comments (trailing or whole-line).
|
||||
@visibleForTesting
|
||||
String strip(String line) => line.replaceAll(_comment, '').trim();
|
||||
String strip(String line, {bool includeQuotes = false}) =>
|
||||
line.replaceAll(includeQuotes ? _commentWithQuotes : _comment, '').trim();
|
||||
|
||||
/// Omits 'export' keyword.
|
||||
@visibleForTesting
|
||||
String swallow(String line) => line.replaceAll(_keyword, '').trim();
|
||||
String swallow(String line) => line.replaceAll(_leadingExport, '').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) =>
|
||||
/// [ 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;
|
||||
}
|
||||
|
292
pubspec.lock
292
pubspec.lock
@ -1,55 +1,111 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "14.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.41.2"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.5.0-nullsafety.1"
|
||||
version: "2.5.0-nullsafety.3"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.3"
|
||||
version: "1.1.0-nullsafety.5"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0-nullsafety.3"
|
||||
version: "1.15.0-nullsafety.5"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.15.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.2.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -60,87 +116,283 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.4"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.3-nullsafety.3"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.11.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.10-nullsafety.1"
|
||||
version: "0.12.10-nullsafety.3"
|
||||
meta:
|
||||
dependency: "direct main"
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.6"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7"
|
||||
node_interop:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_interop
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
node_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.13"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.1"
|
||||
version: "1.8.0-nullsafety.3"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pedantic
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety.3"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.0-nullsafety.3"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.9"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.9+2"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.4"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.10-nullsafety.3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.8.0-nullsafety.2"
|
||||
version: "1.8.0-nullsafety.4"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0-nullsafety.1"
|
||||
version: "1.10.0-nullsafety.6"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
version: "2.1.0-nullsafety.3"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
version: "1.1.0-nullsafety.3"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
version: "1.2.0-nullsafety.3"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.16.0-nullsafety.17"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.19-nullsafety.2"
|
||||
version: "0.2.19-nullsafety.6"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.12-nullsafety.15"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
version: "1.3.0-nullsafety.5"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.3"
|
||||
version: "2.1.0-nullsafety.5"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7+15"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
sdks:
|
||||
dart: ">=2.10.0-110 <2.11.0"
|
||||
dart: ">=2.12.0-0.0 <3.0.0"
|
||||
|
@ -1,14 +1,15 @@
|
||||
name: flutter_dotenv
|
||||
version: 3.1.0
|
||||
version: 4.0.0-nullsafety.0
|
||||
description: Easily configure any flutter application with global variables using a `.env` file.
|
||||
author: java-james <james-collins@hotmail.co.nz>
|
||||
homepage: https://github.com/java-james/flutter_dotenv
|
||||
environment:
|
||||
sdk: '>=2.0.0 <3.0.0'
|
||||
sdk: '>=2.12.0-0 <3.0.0'
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
meta: ^1.1.6
|
||||
|
||||
dev_dependencies:
|
||||
test:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
31
test/.env
Normal file
31
test/.env
Normal file
@ -0,0 +1,31 @@
|
||||
FOO=foo
|
||||
BAR=bar
|
||||
FOOBAR=\$FOO$BAR
|
||||
ESCAPED_DOLLAR_SIGN="\$1000"
|
||||
ESCAPED_QUOTE='\''
|
||||
|
||||
BASIC=basic
|
||||
|
||||
BASIC=basic1
|
||||
|
||||
# previous line intentionally left blank
|
||||
AFTER_LINE=after_line
|
||||
EMPTY=
|
||||
SINGLE_QUOTES='single_quotes'
|
||||
SINGLE_QUOTES_SPACED=' single quotes '
|
||||
DOUBLE_QUOTES="double_quotes"
|
||||
DOUBLE_QUOTES_SPACED=" double quotes "
|
||||
EXPAND_NEWLINES="expand\nnew\nlines"
|
||||
DONT_EXPAND_UNQUOTED=dontexpand\nnewlines
|
||||
DONT_EXPAND_SQUOTED='dontexpand\nnewlines'
|
||||
# COMMENTS=work
|
||||
EQUAL_SIGNS=equals==
|
||||
RETAIN_INNER_QUOTES={"foo": "bar"}
|
||||
RETAIN_LEADING_DQUOTE="retained
|
||||
RETAIN_LEADING_SQUOTE='retained
|
||||
RETAIN_TRAILING_DQUOTE=retained"
|
||||
RETAIN_TRAILING_SQUOTE=retained'
|
||||
RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}'
|
||||
TRIM_SPACE_FROM_UNQUOTED= some spaced out string
|
||||
USERNAME=therealnerdybeast@example.tld
|
||||
SPACED_KEY = parsed
|
42
test/dotenv_test.dart
Normal file
42
test/dotenv_test.dart
Normal file
@ -0,0 +1,42 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart' as dotenv;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('dotenv', () {
|
||||
setUp(() {
|
||||
print(Directory.current.toString());
|
||||
dotenv.testLoad(
|
||||
fileInput: File('.env')
|
||||
.readAsStringSync()); //, mergeWith: Platform.environment
|
||||
});
|
||||
test('able to load .env', () {
|
||||
expect(dotenv.env['FOO'], 'foo');
|
||||
expect(dotenv.env['BAR'], 'bar');
|
||||
expect(dotenv.env['FOOBAR'], '\$FOObar');
|
||||
expect(dotenv.env['ESCAPED_DOLLAR_SIGN'], '\$1000');
|
||||
expect(dotenv.env['ESCAPED_QUOTE'], "'");
|
||||
expect(dotenv.env['BASIC'], 'basic');
|
||||
expect(dotenv.env['AFTER_LINE'], 'after_line');
|
||||
expect(dotenv.env['EMPTY'], '');
|
||||
expect(dotenv.env['SINGLE_QUOTES'], 'single_quotes');
|
||||
expect(dotenv.env['SINGLE_QUOTES_SPACED'], ' single quotes ');
|
||||
expect(dotenv.env['DOUBLE_QUOTES'], 'double_quotes');
|
||||
expect(dotenv.env['DOUBLE_QUOTES_SPACED'], ' double quotes ');
|
||||
expect(dotenv.env['EXPAND_NEWLINES'], "expand\nnew\nlines");
|
||||
expect(dotenv.env['DONT_EXPAND_UNQUOTED'], 'dontexpand\\nnewlines');
|
||||
expect(dotenv.env['DONT_EXPAND_SQUOTED'], 'dontexpand\\nnewlines');
|
||||
expect(dotenv.env['COMMENTS'], null);
|
||||
expect(dotenv.env['EQUAL_SIGNS'], 'equals==');
|
||||
expect(dotenv.env['RETAIN_INNER_QUOTES'], '{"foo": "bar"}');
|
||||
expect(dotenv.env['RETAIN_LEADING_DQUOTE'], "\"retained");
|
||||
expect(dotenv.env['RETAIN_LEADING_SQUOTE'], '\'retained');
|
||||
expect(dotenv.env['RETAIN_TRAILING_DQUOTE'], 'retained\"');
|
||||
expect(dotenv.env['RETAIN_TRAILING_SQUOTE'], "retained\'");
|
||||
expect(dotenv.env['RETAIN_INNER_QUOTES_AS_STRING'], '{"foo": "bar"}');
|
||||
expect(dotenv.env['TRIM_SPACE_FROM_UNQUOTED'], 'some spaced out string');
|
||||
expect(dotenv.env['USERNAME'], 'therealnerdybeast@example.tld');
|
||||
expect(dotenv.env['SPACED_KEY'], 'parsed');
|
||||
});
|
||||
});
|
||||
}
|
170
test/parser_test.dart
Normal file
170
test/parser_test.dart
Normal file
@ -0,0 +1,170 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter_dotenv/src/parser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
const ceil = 100000;
|
||||
|
||||
void main() {
|
||||
late Random rand;
|
||||
const _psr = Parser();
|
||||
group('[Parser]', () {
|
||||
setUp(() => rand = Random());
|
||||
test('it swallows leading "export"', () {
|
||||
var out = _psr.swallow(' export foo = bar ');
|
||||
expect(out, equals('foo = bar'));
|
||||
|
||||
out = _psr.swallow(' foo = bar export');
|
||||
expect(out, equals('foo = bar export'));
|
||||
});
|
||||
|
||||
test('it strips trailing comments', () {
|
||||
var out = _psr.strip(
|
||||
'needs="explanation" # It was the year when they finally immanentized the Eschaton.');
|
||||
expect(out, equals('needs="explanation"'));
|
||||
out = _psr.strip(
|
||||
'needs="explanation # It was the year when they finally immanentized the Eschaton." ');
|
||||
expect(
|
||||
out,
|
||||
equals(
|
||||
'needs="explanation # It was the year when they finally immanentized the Eschaton."'));
|
||||
out = _psr.strip(
|
||||
'needs=explanation # It was the year when they finally immanentized the Eschaton."',
|
||||
includeQuotes: true);
|
||||
expect(out, equals('needs=explanation'));
|
||||
out = _psr.strip(' # It was the best of times, it was a waste of time.');
|
||||
expect(out, isEmpty);
|
||||
});
|
||||
test('it knows quoted # is not a comment', () {
|
||||
var doub = _psr.parseOne('foo = "ab#c"');
|
||||
var single = _psr.parseOne("foo = 'ab#c'");
|
||||
|
||||
expect(doub['foo'], equals('ab#c'));
|
||||
expect(single['foo'], equals('ab#c'));
|
||||
});
|
||||
test('it handles quotes in a comment', () {
|
||||
// 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'));
|
||||
});
|
||||
test('treats all # in unquoted as comments', () {
|
||||
var fail =
|
||||
_psr.parseOne('fruit = banana # I\'m a comment with a final "quote"');
|
||||
expect(fail['fruit'], equals('banana'));
|
||||
});
|
||||
|
||||
test('it handles unquoted values', () {
|
||||
var out = _psr.unquote(' str ');
|
||||
expect(out, equals('str'));
|
||||
});
|
||||
test('it handles double quoted values', () {
|
||||
var out = _psr.unquote('"val "');
|
||||
expect(out, equals('val '));
|
||||
});
|
||||
test('it handles single quoted values', () {
|
||||
var out = _psr.unquote("' val'");
|
||||
expect(out, equals(' val'));
|
||||
});
|
||||
test('retain trailing single quote', () {
|
||||
var out = _psr.unquote("retained'");
|
||||
expect(out, equals("retained'"));
|
||||
});
|
||||
|
||||
// test('it handles escaped quotes within values', () { // Does not
|
||||
// var out = _psr.unquote('''\'val_with_\\"escaped\\"_\\'quote\\'s \'''');
|
||||
// expect(out, equals('''val_with_"escaped"_'quote's '''));
|
||||
// out = _psr.unquote(" val_with_\"escaped\"_\'quote\'s ");
|
||||
// expect(out, equals('''val_with_"escaped"_'quote's'''));
|
||||
// });
|
||||
|
||||
test('it skips empty lines', () {
|
||||
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'}));
|
||||
});
|
||||
|
||||
test('it ignores duplicate keys', () {
|
||||
var out = _psr.parse(['foo=bar', 'foo=baz']);
|
||||
expect(out, equals({'foo': 'bar'}));
|
||||
});
|
||||
test('it substitutes known variables into other values', () {
|
||||
var out = _psr.parse(['foo=bar', r'baz=super$foo']);
|
||||
expect(out, equals({'foo': 'bar', 'baz': 'superbar'}));
|
||||
});
|
||||
test('it discards surrounding quotes', () {
|
||||
var out = _psr.parse([r"foo = 'bar'", r'export baz="qux"']);
|
||||
expect(out, equals({'foo': 'bar', 'baz': 'qux'}));
|
||||
});
|
||||
|
||||
test('it detects unquoted values', () {
|
||||
var out = _psr.surroundingQuote('no quotes here!');
|
||||
expect(out, isEmpty);
|
||||
});
|
||||
test('it detects double-quoted values', () {
|
||||
var out = _psr.surroundingQuote('"double quoted"');
|
||||
expect(out, equals('"'));
|
||||
});
|
||||
test('it detects single-quoted values', () {
|
||||
var out = _psr.surroundingQuote("'single quoted'");
|
||||
expect(out, equals("'"));
|
||||
});
|
||||
|
||||
test('it performs variable substitution', () {
|
||||
var out = _psr.interpolate(r'a$foo$baz', {'foo': 'bar', 'baz': 'qux'});
|
||||
expect(out, equals('abarqux'));
|
||||
});
|
||||
test('it skips undefined variables', () {
|
||||
var r = rand.nextInt(ceil); // avoid runtime collision with real env vars
|
||||
var out = _psr.interpolate('a\$jinx_$r', {});
|
||||
expect(out, equals('a'));
|
||||
});
|
||||
test('it handles explicitly null values in env', () {
|
||||
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'));
|
||||
});
|
||||
test('it handles \${surrounding braces} on vars', () {
|
||||
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'));
|
||||
});
|
||||
|
||||
test('it handles equal signs in values', () {
|
||||
var none = _psr.parseOne('foo=bar=qux');
|
||||
var sing = _psr.parseOne("foo='bar=qux'");
|
||||
var doub = _psr.parseOne('foo="bar=qux"');
|
||||
|
||||
expect(none['foo'], equals('bar=qux'));
|
||||
expect(sing['foo'], equals('bar=qux'));
|
||||
expect(doub['foo'], equals('bar=qux'));
|
||||
});
|
||||
|
||||
test('it skips var substitution in single quotes', () {
|
||||
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'));
|
||||
});
|
||||
test('it performs var subs in double quotes', () {
|
||||
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'));
|
||||
});
|
||||
test('it performs var subs without quotes', () {
|
||||
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'));
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user