-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.coffee
138 lines (122 loc) · 4.95 KB
/
index.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
injector = require('def-type').Module ->
# Private Shared data
definedModule = null
config = {bypassInjection: true}
ENV = 'production'
path = require('path')
cwd = process.cwd()
###*
* @public
* Sets a wrapper to add variables that use @import inside the passed fn to require modules,
* this allows to inject dependencies via the fn wrapper instead of the actually required
* modules.
* @param {function} wrapperFn - Function where the return value is what is exported later
###
@set = (wrapperFn)->
# We add the import method to the passed fn to allow the end user to call it with in that fn
wrapperFn.import = _wrapperImport
wrapperFn.importGlobal = _importGlobal
if config.bypassInjection
definedModule = wrapperFn.call(wrapperFn)
else
#definedModule = wrapperFn.bind(wrapperFn)
definedModule = (dependencies)->
wrapperFn.dependencies = dependencies;
wrapperFn.call(wrapperFn)
# Used by @import to check if the imported module is an injector wrapper
definedModule.__injectorWrapper__ = true
return definedModule
###*
* @public
* Returns the defined module, which could be the wrapper fn defined in the @set method
* or what ever was returned from that function, depending of the setting of the byPassInjection option
* in the config object.
###
@get = ->
t = definedModule
definedModule = null
return t
###*
* @public
* When defined as true (default), whenever you set an injector wrapper, you set the defined module
* to whatever is returned by that wrapper, and when set to false you get the wrapper instead. This
* wrapper function is what allows you to inject dependencies by passing a single object to it
###
@bypassInjection = (boolean)->
config.bypassInjection = boolean
return this
###*
* @public
* Lets you set in a more declarative way that you want to bypass or not the injection system.
* @example require('commonjs-injector').setEnv('testing') should be used before running your
* test, and a regular require without .setEnv or require('commonjs-injector').setEnv('production')
* will set to true the bypassing.
###
@setEnv = (environment)->
ENV = environment.toLowerCase()
switch ENV
when "production" then config.bypassInjection = true
when "testing" then config.bypassInjection = false
else
errMsg = 'You should set the environment of the injector as either testing or production'
throw new Error errMsg
return this
@getEnv = -> ENV
###*
* @public
* Mimics node require behavior, with some subtle differences:
* Files are required relative to the cwd
###
@import = (pathFragments...)->
_import(pathFragments)
###*
* @private
* This function is added to the wrapperFn (i.e wrapperFn.import) to use instead of the node.js
* require function, this allows to bypass the actual module requiring by injecting the module
* in the dependencies obj of the wrapperFn.
###
_wrapperImport = (pathFragments...)->
moduleName = _getModuleName(pathFragments)
# @dependencies refers to the one defined in the wrapperFn not in the injector module
if @dependencies?[moduleName]?
return @dependencies[moduleName]
else
_import(pathFragments)
_getModuleName = (pathFragments)->
moduleName = pathFragments[pathFragments.length - 1].split('/').pop()
_import = (pathFragments)->
fragsLen = pathFragments.length
# When no slashes found we assume is an npm module
isNpmModule = pathFragments[0].indexOf('/') is -1
# When a single argument is provided to the import fn
# we check if is an npm module, if not we assume a regular
# one and provide the cwd as the base of the path
if fragsLen is 1
if isNpmModule
filePath = pathFragments[0]
else
pathFragments.splice(0, 0, cwd)
filePath = path.resolve.apply(@, pathFragments)
else
filePath = path.resolve.apply(@, pathFragments)
module = require(filePath)
# When the imported/required module is being wrapped by an injector fn, and the env is testing
# we should get its contents (The actual module, not the wrapper).
if module.__injectorWrapper__?
module = module()
return module
###*
* @private
* This function is added to the wrapperFn (i.e wrapperFn.importGlobal) to define global variables
* as local ones, this allows to inject whatever we want in wrapperFn as long as we use the same name.
* @example @importGlobal('async') will call global.async internally or the injected value
* @TODO Should allow subobject keys, meaning that you should be able to call @importGlobal('someGloba.key.anotherKey'),
* which right now you can't. Just first lvl globals.
###
_importGlobal = (globalName)->
# @dependencies refers to the one defined in the wrapperFn not in the injector module
if @dependencies?[globalName]?
@dependencies[globalName]
else
global[globalName]
module.exports = injector