11require "dotenvx/version"
22require "dotenvx/dotenvx_native"
33require "json"
4+ require "pathname"
45
56module Dotenvx
67 class MissingKeys < RuntimeError
@@ -16,8 +17,14 @@ class << self
1617 attr_accessor :instrumenter
1718
1819 def load ( *filenames , overwrite : false , ignore : true )
19- env = parse ( *filenames , overwrite : overwrite , ignore : ignore )
20- update ( env , overwrite : overwrite )
20+ env , injected_keys , loaded_paths = parse_files (
21+ *filenames ,
22+ overwrite : overwrite ,
23+ ignore : ignore
24+ )
25+ changed = update ( env , overwrite : overwrite )
26+ log_injected ( injected_keys , loaded_paths )
27+ changed
2128 end
2229
2330 def load! ( *filenames )
@@ -35,31 +42,7 @@ def overwrite!(*filenames)
3542 alias overload! overwrite!
3643
3744 def parse ( *filenames , overwrite : false , ignore : true )
38- filenames = [ ".env" ] if filenames . empty?
39- filenames = filenames . flatten . reverse if overwrite
40-
41- process_env = ENV . to_h
42- filenames . reduce ( { } ) do |values , filename |
43- path = File . expand_path ( filename )
44- begin
45- source = File . binread ( path ) . sub ( /\A \xEF \xBB \xBF / , "" ) . force_encoding ( Encoding ::UTF_8 )
46- rescue Errno ::ENOENT , Errno ::EISDIR
47- raise unless ignore
48- next values
49- end
50-
51- parsed , = Native . parse_dotenv ( JSON . generate (
52- source : source ,
53- process_env : process_env ,
54- overwrite : overwrite == true ,
55- key_files : key_files ( path )
56- ) )
57- parsed = parsed . to_h
58- process_env . merge! ( parsed )
59- values . merge! ( parsed )
60- yield Environment . new ( path , parsed ) if block_given?
61- values
62- end
45+ parse_files ( *filenames , overwrite : overwrite , ignore : ignore ) . first
6346 end
6447
6548 def update ( env = { } , overwrite : false )
@@ -89,6 +72,56 @@ def require_keys(*keys)
8972
9073 private
9174
75+ def parse_files ( *filenames , overwrite : false , ignore : true )
76+ filenames = [ ".env" ] if filenames . empty?
77+ filenames = filenames . flatten . reverse if overwrite
78+
79+ process_env = ENV . to_h
80+ injected_keys = { }
81+ loaded_paths = [ ]
82+ values = filenames . reduce ( { } ) do |accumulator , filename |
83+ path = File . expand_path ( filename )
84+ begin
85+ source = File . binread ( path ) . sub ( /\A \xEF \xBB \xBF / , "" ) . force_encoding ( Encoding ::UTF_8 )
86+ rescue Errno ::ENOENT , Errno ::EISDIR
87+ raise unless ignore
88+ next accumulator
89+ end
90+
91+ parsed , injected = Native . parse_dotenv ( JSON . generate (
92+ source : source ,
93+ process_env : process_env ,
94+ overwrite : overwrite == true ,
95+ key_files : key_files ( path )
96+ ) )
97+ parsed = parsed . to_h
98+ injected . each { |key , _value | injected_keys [ key ] = true }
99+ loaded_paths << path
100+ process_env . merge! ( parsed )
101+ accumulator . merge! ( parsed )
102+ yield Environment . new ( path , parsed ) if block_given?
103+ accumulator
104+ end
105+ [ values , injected_keys . keys , loaded_paths ]
106+ end
107+
108+ def log_injected ( injected_keys , loaded_paths )
109+ message = "⟐ injected env (#{ injected_keys . length } )"
110+ unless loaded_paths . empty?
111+ paths = loaded_paths . map { |path | readable_path ( path ) }
112+ message = "#{ message } from #{ paths . join ( ", " ) } "
113+ end
114+ warn message
115+ end
116+
117+ def readable_path ( path )
118+ pathname = Pathname . new ( path )
119+ relative = pathname . relative_path_from ( Pathname . pwd ) . to_s
120+ relative . start_with? ( "../" ) ? pathname . to_s : relative
121+ rescue ArgumentError
122+ pathname . to_s
123+ end
124+
92125 def key_files ( path )
93126 candidates = [ "#{ path } .keys" , File . join ( File . dirname ( path ) , ".env.keys" ) ]
94127 candidates . select { |candidate | File . file? ( candidate ) }
0 commit comments