7676declare __go_module_name
7777declare __go_module_file
7878declare __go_loaded_module
79+ declare __go_loaded_file
80+ declare __go_module_index
81+ declare __go_use_caller
82+ declare __go_use_prev_caller
7983
80- for __go_module_name in " $@ " ; do
81- for __go_loaded_module in " ${_GO_IMPORTED_MODULES[@]} " ; do
82- if [[ " $__go_module_name " == " $__go_loaded_module " ]]; then
83- continue 2
84+ # Wrapper function necessary due to BASH_SOURCE and FUNCNAME bug in Bash <4.3.
85+ _@go.set_use_caller () {
86+ __go_use_caller=" ${BASH_SOURCE[2]} :${BASH_LINENO[1]} ${FUNCNAME[2]} "
87+ }
88+
89+ _@go.find_plugin_module () {
90+ [[ -f " $1 /$__go_module_file " ]] && __go_module_file=" $1 /$__go_module_file "
91+ }
92+
93+ _@go.find_module () {
94+ # If a script imports a plugin module, and that module (`__go_use_caller`)
95+ # tries to import another module from the same plugin, this block will adjust
96+ # the search parameters accordingly.
97+ if [[ -n " $_GO_PLUGINS_DIR " &&
98+ " $__go_use_caller " =~ ^$_GO_PLUGINS_DIR /.* /lib/ ]]; then
99+ local _GO_SCRIPTS_DIR=" ${__go_use_caller%/ lib/* } /bin"
100+ local _GO_ROOTDIR=" ${_GO_SCRIPTS_DIR%/ bin} "
101+ fi
102+ __go_module_file=" $_GO_SCRIPTS_DIR /lib/$__go_module_name "
103+
104+ if [[ ! -f " $__go_module_file " ]]; then
105+ __go_module_file=" $_GO_ROOTDIR /lib/$__go_module_name "
106+
107+ if [[ ! -f " $__go_module_file " ]]; then
108+ # Convert <plugin>/<module> to plugins/<plugin>/lib/<module>
109+ __go_module_file=" ${__go_module_name/ \/ // lib/ } "
110+ @go.search_plugins ' _@go.find_plugin_module'
84111 fi
85- done
112+ fi
113+ }
86114
87- # Prevent self- and circular importing by registering name before sourcing.
88- _GO_IMPORTED_MODULES+=(" $__go_module_name " )
89- __go_module_file=' '
115+ for __go_module_name in " $@ " ; do
116+ # Since every import is in the same scope, setting the caller each time is
117+ # necessary in case any imported modules import other modules.
118+ _@go.set_use_caller
119+ __go_module_file=" $_GO_CORE_DIR /lib/$__go_module_name "
90120
91121 if [[ -n " $_GO_INJECT_MODULE_PATH " ]]; then
92122 __go_module_file=" $_GO_INJECT_MODULE_PATH /$__go_module_name "
93123 if [[ ! -f " $__go_module_file " ]]; then
94- __go_module_file=' '
124+ __go_module_file=" $_GO_CORE_DIR /lib/ $__go_module_name "
95125 fi
96126 fi
97- __go_module_file=" ${__go_module_file:- $_GO_CORE_DIR / lib/ $__go_module_name } "
98127
99- if [[ ! -f " $__go_module_file " ]]; then
100- __go_module_file=" $_GO_SCRIPTS_DIR /lib/$__go_module_name "
128+ if [[ ! -f " $__go_module_file " ]] && ! _@go.find_module; then
129+ @go.printf ' ERROR: Module %s not found at:\n' " $__go_module_name " >&2
130+ @go.print_stack_trace 1 >&2
131+ exit 1
132+ fi
101133
102- if [[ ! -f " $__go_module_file " ]]; then
103- __go_module_file=" $_GO_ROOTDIR /lib/$__go_module_name "
134+ # If we found the module in our project, but we're installed as a plugin,
135+ # change the loaded module name to reflect that.
136+ #
137+ # Using `##` to trim the string instead of `#` flattens the module name
138+ # namespace. We could use `#` to keep it hierarchical, but since the Bash
139+ # namespace itself is flat, this might lead to hard-to-debug collisions if
140+ # functions and variables get redefined. Keeping a flat module name namespace
141+ # allows us to detect such potential collisions and issue a warning below.
142+ if [[ -n " $_GO_PLUGINS_DIR " &&
143+ " $__go_module_file " =~ ^$_GO_PLUGINS_DIR ]]; then
144+ __go_module_name=" ${__go_module_file##*/ plugins/ } "
145+ __go_module_name=" ${__go_module_name/ \/ bin\/ // } "
146+ __go_module_name=" ${__go_module_name/ \/ lib\/ // } "
147+ fi
104148
105- if [[ ! -f " $__go_module_file " ]]; then
106- # Convert <plugin>/<module> to plugins/<plugin>/lib/<module>
107- __go_module_file=" $_GO_SCRIPTS_DIR /plugins/${__go_module_name/ \/ // lib/ } "
149+ __go_module_index=0
150+ for __go_loaded_module in " ${_GO_IMPORTED_MODULES[@]} " ; do
151+ if [[ " $__go_module_name " == " $__go_loaded_module " ]]; then
152+ __go_loaded_file=" ${_GO_IMPORTED_MODULE_FILES[$__go_module_index]} "
153+ __go_use_prev_caller=" ${_GO_IMPORTED_MODULE_CALLERS[$__go_module_index]} "
108154
109- if [[ ! -f " $__go_module_file " ]]; then
110- @go.printf ' ERROR: Module %s not found at:\n' " $__go_module_name " >&2
111- @go.print_stack_trace 1 >&2
112- exit 1
113- fi
155+ # This may happen if a plugin appears more than once in a project tree.
156+ if [[ " $__go_module_file " != " $__go_loaded_file " ]]; then
157+ @go.printf ' %s\n' " WARNING: Module: $__go_module_name " \
158+ " imported at: $__go_use_caller " \
159+ " from file: $__go_module_file " \
160+ " previously imported at: $__go_use_prev_caller " \
161+ " from file: $__go_loaded_file " >&2
114162 fi
163+ continue 2
115164 fi
116- fi
165+ (( ++ __go_module_index))
166+ done
167+
168+ # Prevent self- and circular importing by registering info before sourcing.
169+ _GO_IMPORTED_MODULES+=(" $__go_module_name " )
170+ _GO_IMPORTED_MODULE_FILES+=(" $__go_module_file " )
171+ _GO_IMPORTED_MODULE_CALLERS+=(" $__go_use_caller " )
117172
118173 if ! . " $__go_module_file " ; then
119174 @go.printf ' ERROR: Failed to import %s module from %s at:\n' \
@@ -123,6 +178,10 @@ for __go_module_name in "$@"; do
123178 fi
124179done
125180
181+ unset __go_use_prev_caller
182+ unset __go_use_caller
183+ unset __go_module_index
184+ unset __go_loaded_file
126185unset __go_loaded_module
127186unset __go_module_file
128187unset __go_module_name
0 commit comments