From 7e450198070b2b802e7a264ae8a202c33c5216a9 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Wed, 2 Feb 2022 12:44:27 -0800 Subject: [PATCH 1/2] Add support for backticks --- lib/main.js | 4 ++-- tests/.env | 11 +++++++++++ tests/.env-multiline | 6 ++++++ tests/test-parse-multiline.js | 2 ++ tests/test-parse.js | 20 ++++++++++++++++++++ 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/main.js b/lib/main.js index 34facb25..87fedd4c 100644 --- a/lib/main.js +++ b/lib/main.js @@ -2,7 +2,7 @@ const fs = require('fs') const path = require('path') const os = require('os') -const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg +const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg // Parser src into an Object function parse (src) { @@ -28,7 +28,7 @@ function parse (src) { const maybeQuote = value[0] // Remove surrounding quotes - value = value.replace(/^(['"])([\s\S]*)\1$/mg, '$2') + value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2') // Expand newlines if double quoted if (maybeQuote === '"') { diff --git a/tests/.env b/tests/.env index 241c88fd..33256d44 100644 --- a/tests/.env +++ b/tests/.env @@ -5,23 +5,34 @@ AFTER_LINE=after_line EMPTY= EMPTY_SINGLE_QUOTES='' EMPTY_DOUBLE_QUOTES="" +EMPTY_BACKTICKS=`` SINGLE_QUOTES='single_quotes' SINGLE_QUOTES_SPACED=' single quotes ' DOUBLE_QUOTES="double_quotes" DOUBLE_QUOTES_SPACED=" double quotes " DOUBLE_QUOTES_INSIDE_SINGLE='double "quotes" work inside single quotes' SINGLE_QUOTES_INSIDE_DOUBLE="single 'quotes' work inside double quotes" +BACKTICKS_INSIDE_SINGLE='`backticks` work inside single quotes' +BACKTICKS_INSIDE_DOUBLE="`backticks` work inside double quotes" +BACKTICKS=`backticks` +BACKTICKS_SPACED=` backticks ` +DOUBLE_QUOTES_INSIDE_BACKTICKS=`double "quotes" work inside backticks` +SINGLE_QUOTES_INSIDE_BACKTICKS=`single 'quotes' work inside backticks` +DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS=`double "quotes" and single 'quotes' work inside backticks` EXPAND_NEWLINES="expand\nnew\nlines" DONT_EXPAND_UNQUOTED=dontexpand\nnewlines DONT_EXPAND_SQUOTED='dontexpand\nnewlines' +DONT_EXPAND_SQUOTED='dontexpand\nnewlines' # COMMENTS=work INLINE_COMMENTS=inline comments # work #very #well INLINE_COMMENTS_SINGLE_QUOTES='inline comments outside of #singlequotes' # work INLINE_COMMENTS_DOUBLE_QUOTES="inline comments outside of #doublequotes" # work +INLINE_COMMENTS_BACKTICKS=`inline comments outside of #backticks` # work INLINE_COMMENTS_SPACE=inline comments start with a#space EQUAL_SIGNS=equals== RETAIN_INNER_QUOTES={"foo": "bar"} RETAIN_INNER_QUOTES_AS_STRING='{"foo": "bar"}' +RETAIN_INNER_QUOTES_AS_BACKTICKS=`{"foo": "bar's"}` TRIM_SPACE_FROM_UNQUOTED= some spaced out string USERNAME=therealnerdybeast@example.tld SPACED_KEY = parsed diff --git a/tests/.env-multiline b/tests/.env-multiline index 758b3377..f4deacd6 100644 --- a/tests/.env-multiline +++ b/tests/.env-multiline @@ -30,3 +30,9 @@ IS A MULTILINE STRING' + +MULTI_BACKTICKED=`THIS +IS +A +"MULTILINE'S" +STRING` diff --git a/tests/test-parse-multiline.js b/tests/test-parse-multiline.js index c2954598..67e18c40 100644 --- a/tests/test-parse-multiline.js +++ b/tests/test-parse-multiline.js @@ -45,6 +45,8 @@ t.equal(parsed.MULTI_DOUBLE_QUOTED, 'THIS\nIS\nA\nMULTILINE\nSTRING', 'parses mu t.equal(parsed.MULTI_SINGLE_QUOTED, 'THIS\nIS\nA\nMULTILINE\nSTRING', 'parses multi-line strings when using single quotes') +t.equal(parsed.MULTI_BACKTICKED, 'THIS\nIS\nA\n"MULTILINE\'S"\nSTRING', 'parses multi-line strings when using single quotes') + const payload = dotenv.parse(Buffer.from('BUFFER=true')) t.equal(payload.BUFFER, 'true', 'should parse a buffer into an object') diff --git a/tests/test-parse.js b/tests/test-parse.js index 7526ecfa..544a7282 100644 --- a/tests/test-parse.js +++ b/tests/test-parse.js @@ -17,6 +17,8 @@ t.equal(parsed.EMPTY_SINGLE_QUOTES, '', 'defaults empty values to empty string') t.equal(parsed.EMPTY_DOUBLE_QUOTES, '', 'defaults empty values to empty string') +t.equal(parsed.EMPTY_BACKTICKS, '', 'defaults empty values to empty string') + t.equal(parsed.SINGLE_QUOTES, 'single_quotes', 'escapes single quoted values') t.equal(parsed.SINGLE_QUOTES_SPACED, ' single quotes ', 'respects surrounding spaces in single quotes') @@ -29,6 +31,20 @@ t.equal(parsed.DOUBLE_QUOTES_INSIDE_SINGLE, 'double "quotes" work inside single t.equal(parsed.SINGLE_QUOTES_INSIDE_DOUBLE, "single 'quotes' work inside double quotes", 'respects single quotes inside double quotes') +t.equal(parsed.BACKTICKS_INSIDE_SINGLE, '`backticks` work inside single quotes', 'respects backticks inside single quotes') + +t.equal(parsed.BACKTICKS_INSIDE_DOUBLE, '`backticks` work inside double quotes', 'respects backticks inside double quotes') + +t.equal(parsed.BACKTICKS, 'backticks') + +t.equal(parsed.BACKTICKS_SPACED, ' backticks ') + +t.equal(parsed.DOUBLE_QUOTES_INSIDE_BACKTICKS, 'double "quotes" work inside backticks', 'respects double quotes inside backticks') + +t.equal(parsed.SINGLE_QUOTES_INSIDE_BACKTICKS, "single 'quotes' work inside backticks", 'respects single quotes inside backticks') + +t.equal(parsed.DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS, "double \"quotes\" and single 'quotes' work inside backticks", 'respects single quotes inside backticks') + t.equal(parsed.EXPAND_NEWLINES, 'expand\nnew\nlines', 'expands newlines but only if double quoted') t.equal(parsed.DONT_EXPAND_UNQUOTED, 'dontexpand\\nnewlines', 'expands newlines but only if double quoted') @@ -43,6 +59,8 @@ t.equal(parsed.INLINE_COMMENTS_SINGLE_QUOTES, 'inline comments outside of #singl t.equal(parsed.INLINE_COMMENTS_DOUBLE_QUOTES, 'inline comments outside of #doublequotes', 'ignores inline comments and respects # character inside of double quotes') +t.equal(parsed.INLINE_COMMENTS_BACKTICKS, 'inline comments outside of #backticks', 'ignores inline comments and respects # character inside of backticks') + t.equal(parsed.INLINE_COMMENTS_SPACE, 'inline comments start with a', 'treats # character as start of comment') t.equal(parsed.EQUAL_SIGNS, 'equals==', 'respects equals signs in values') @@ -55,6 +73,8 @@ t.equal(parsed.RETAIN_INNER_QUOTES, '{"foo": "bar"}', 'retains inner quotes') t.equal(parsed.RETAIN_INNER_QUOTES_AS_STRING, '{"foo": "bar"}', 'retains inner quotes') +t.equal(parsed.RETAIN_INNER_QUOTES_AS_BACKTICKS, '{"foo": "bar\'s"}', 'retains inner quotes') + t.equal(parsed.TRIM_SPACE_FROM_UNQUOTED, 'some spaced out string', 'retains spaces in string') t.equal(parsed.USERNAME, 'therealnerdybeast@example.tld', 'parses email addresses completely') From a8d34e57420a0a00f51e22e35878b7797c471334 Mon Sep 17 00:00:00 2001 From: Scott Motte Date: Wed, 2 Feb 2022 12:56:10 -0800 Subject: [PATCH 2/2] Update README --- CHANGELOG.md | 8 +++++++- README.md | 2 ++ package-lock.json | 4 ++-- package.json | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e350818d..b500eddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,13 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. -## [Unreleased](https://github.com/motdotla/dotenv/compare/v15.0.0...master) +## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.0.0...master) + +## [16.0.0](https://github.com/motdotla/dotenv/compare/v15.0.1...v16.0.0) (2022-02-02) + +### Added + +- _Breaking:_ Backtick support 🎉 (template literals). If you had values containing the backtick character, please quote those values with either single or double quotes. ## [15.0.1](https://github.com/motdotla/dotenv/compare/v15.0.0...v15.0.1) (2022-02-02) diff --git a/README.md b/README.md index 9e993f22..8b51f70a 100644 --- a/README.md +++ b/README.md @@ -299,6 +299,8 @@ The parsing engine currently supports the following rules: line'} ``` +- backticks are supported (`BACKTICK_KEY=`This has 'single' and "double" quotes inside of it.``) + ### What happens to environment variables that were already set? By default, we will never modify any environment variables that have already been set. In particular, if there is a variable in your `.env` file which collides with one that already exists in your environment, then that variable will be skipped. diff --git a/package-lock.json b/package-lock.json index de80a80b..caef27ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { "name": "dotenv", - "version": "15.0.1", + "version": "16.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "version": "15.0.1", + "version": "16.0.0", "license": "BSD-2-Clause", "devDependencies": { "@types/node": "^17.0.9", diff --git a/package.json b/package.json index 295755bf..b413ed3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dotenv", - "version": "15.0.1", + "version": "16.0.0", "description": "Loads environment variables from .env file", "main": "lib/main.js", "types": "lib/main.d.ts",