11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4- import * as dotenv from 'dotenv' ;
54import * as fs from 'fs-extra' ;
65import { inject , injectable } from 'inversify' ;
76import * as path from 'path' ;
7+ import { sendTelemetryEvent } from '../../telemetry' ;
8+ import { EventName } from '../../telemetry/constants' ;
89import { IPathUtils } from '../types' ;
910import { EnvironmentVariables , IEnvironmentVariablesService } from './types' ;
1011
@@ -14,14 +15,14 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService
1415 constructor ( @inject ( IPathUtils ) pathUtils : IPathUtils ) {
1516 this . pathVariable = pathUtils . getPathVariableName ( ) ;
1617 }
17- public async parseFile ( filePath ?: string ) : Promise < EnvironmentVariables | undefined > {
18+ public async parseFile ( filePath ?: string , baseVars ?: EnvironmentVariables ) : Promise < EnvironmentVariables | undefined > {
1819 if ( ! filePath || ! await fs . pathExists ( filePath ) ) {
1920 return ;
2021 }
2122 if ( ! fs . lstatSync ( filePath ) . isFile ( ) ) {
2223 return ;
2324 }
24- return dotenv . parse ( await fs . readFile ( filePath ) ) ;
25+ return parseEnvFile ( await fs . readFile ( filePath ) , baseVars ) ;
2526 }
2627 public mergeVariables ( source : EnvironmentVariables , target : EnvironmentVariables ) {
2728 if ( ! target ) {
@@ -61,3 +62,77 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService
6162 return vars ;
6263 }
6364}
65+
66+ export function parseEnvFile (
67+ lines : string | Buffer ,
68+ baseVars ?: EnvironmentVariables
69+ ) : EnvironmentVariables {
70+ const globalVars = baseVars ? baseVars : { } ;
71+ const vars = { } ;
72+ lines . toString ( ) . split ( '\n' ) . forEach ( ( line , idx ) => {
73+ const [ name , value ] = parseEnvLine ( line ) ;
74+ if ( name === '' ) {
75+ return ;
76+ }
77+ vars [ name ] = substituteEnvVars ( value , vars , globalVars ) ;
78+ } ) ;
79+ return vars ;
80+ }
81+
82+ function parseEnvLine ( line : string ) : [ string , string ] {
83+ // Most of the following is an adaptation of the dotenv code:
84+ // https://github.com/motdotla/dotenv/blob/master/lib/main.js#L32
85+ // We don't use dotenv here because it loses ordering, which is
86+ // significant for substitution.
87+ const match = line . match ( / ^ \s * ( [ a - z A - Z ] \w * ) \s * = \s * ( .* ?) ? \s * $ / ) ;
88+ if ( ! match ) {
89+ return [ '' , '' ] ;
90+ }
91+
92+ const name = match [ 1 ] ;
93+ let value = match [ 2 ] ;
94+ if ( value && value !== '' ) {
95+ if ( value [ 0 ] === '\'' && value [ value . length - 1 ] === '\'' ) {
96+ value = value . substring ( 1 , value . length - 1 ) ;
97+ value = value . replace ( / \\ n / gm, '\n' ) ;
98+ } else if ( value [ 0 ] === '"' && value [ value . length - 1 ] === '"' ) {
99+ value = value . substring ( 1 , value . length - 1 ) ;
100+ value = value . replace ( / \\ n / gm, '\n' ) ;
101+ }
102+ } else {
103+ value = '' ;
104+ }
105+
106+ return [ name , value ] ;
107+ }
108+
109+ const SUBST_REGEX = / \$ { ( [ a - z A - Z ] \w * ) ? ( [ ^ } \w ] .* ) ? } / g;
110+
111+ function substituteEnvVars (
112+ value : string ,
113+ localVars : EnvironmentVariables ,
114+ globalVars : EnvironmentVariables ,
115+ missing = ''
116+ ) : string {
117+ // Substitution here is inspired a little by dotenv-expand:
118+ // https://github.com/motdotla/dotenv-expand/blob/master/lib/main.js
119+
120+ let invalid = false ;
121+ let replacement = value ;
122+ replacement = replacement . replace ( SUBST_REGEX , ( match , substName , bogus , offset , orig ) => {
123+ if ( offset > 0 && orig [ offset - 1 ] === '\\' ) {
124+ return match ;
125+ }
126+ if ( ( bogus && bogus !== '' ) || ! substName || substName === '' ) {
127+ invalid = true ;
128+ return match ;
129+ }
130+ return localVars [ substName ] || globalVars [ substName ] || missing ;
131+ } ) ;
132+ if ( ! invalid && replacement !== value ) {
133+ value = replacement ;
134+ sendTelemetryEvent ( EventName . ENVFILE_VARIABLE_SUBSTITUTION ) ;
135+ }
136+
137+ return value . replace ( / \\ \$ / g, '$' ) ;
138+ }
0 commit comments