-
Notifications
You must be signed in to change notification settings - Fork 0
/
npmlocator.ls
150 lines (131 loc) · 4.03 KB
/
npmlocator.ls
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
139
140
141
142
143
144
145
146
147
148
149
150
isFilePath = (name) ->
return true if name[0] == '/'
return true if name.substring(0, 2) == './'
return true if name.substring(0, 3) == '../'
return false
#fetchUrl = (url, method='GET') -> new Promise (accept, reject) ->
# return fetchTextFromURL url.toString(), accept, reject
fetchUrl = (url) ->
System.fetch do
address: url.toString()
metadata: {}
checkUrl = (url) -> fetchUrl url, 'HEAD'
resolvePackage = (pkg) ->
fetchUrl joinPath pkg, "package.json"
.then (data) ->
# TODO: Figure out how to handle the
# browser-field nicely
main = JSON.parse(data).main ? 'index'
if main[*-1] == '/'
main += "index"
return resolveFile joinPath(pkg, main)
resolveFileUrl = (url) ->
checkUrl url .then -> url
resolveFile = (path) ->
resolveFileUrl path
.catch -> resolveFileUrl path + ".js"
resolvePath = (path) ->
# We can't check for directories over HTTP, so
# check first for <path>/package.json and after that
# try to open it as a file. According to the spec
# this should be done another way around, but this
# probably won't be a problem in practice.
resolvePackage path
.catch -> resolveFile path
resolveNodeModule = (name, path='') ->
resolvePackage joinPath path, 'node_modules', name
.catch ->
ppath = parentPath path
while ppath.pathname.split('/')[*-1] == 'node_modules'
ppath = parentPath ppath
resolveNodeModule name, ppath
parentPath = (path) ->
path = new URL path
parts = path.pathname.split('/')
parts = parts.filter (p) -> p not in ['', '.']
if parts.length == 0
throw new Error "No parent path for '#{path}'"
parts.pop()
path.pathname = parts.join '/'
path.search = ''
path.hash = ''
return path
joinPath = (base, ...parts) ->
base = new URL(base.toString!)
parts = parts.filter (p) -> p not in ['', '.']
if base.pathname[*-1] == '/'
base.pathname = base.pathname.slice(0, -1)
parts.unshift base.pathname
base.pathname = parts.join '/'
base.pathname = base.pathname.normalize()
return base
builtins = void
if document?
myPath = parentPath document.getElementsByTagName('script')[*-1].src
else
myPath = parentPath "file://" + __filename
builtinsPath = joinPath myPath, 'node_modules/browser-builtins'
builtinsPromise = fetchUrl(joinPath(builtinsPath, 'package.json'))
.then (data) ->
conf = JSON.parse data
builtins := conf.browser
.then -> System.import("buffer")
.then (buffer) ->
if window?
window.Buffer = buffer.Buffer
# See http://nodejs.org/docs/v0.4.8/api/all.html#all_Together...
nodeResolve = (...args) ->
orig = Promise.resolve(promiseNodeResolve ...args)
orig.then (path) ->
return path
promiseNodeResolve = (...args) ->
# A wrapper where we make sure that we have our async loaded config
if not builtins?
return builtinsPromise.then ->
doNodeResolve ...args
return doNodeResolve ...args
doNodeResolve = (name, parent) ->
if name of builtins
return rawNodeResolve builtins[name], joinPath(builtinsPath, 'dummy')
return rawNodeResolve name, parent
rawNodeResolve = (name, parent) ->
if not parent?
dir = new URL System.baseURL
else
dir = parentPath new URL parent
if isFilePath name
return resolvePath joinPath(dir, name)
resolveNodeModule name, dir
memoize = (f) ->
cache = {}
(...args) ->
key = JSON.stringify args
if key of cache
return cache[key]
return cache[key] = f ...args
absURLRegEx = /^[^\/]+:\/\//
monkeypatch = (System) ->
oldNormalize = System.normalize
resolver = memoize nodeResolve
normalize = (path, parent, isPlugin) ->
if path.match absURLRegEx or path[0] == '@'
return Promise.resolve(path)
parts = path.split '!'
[path, ...plugins] = parts
# TODO: Should extend the existing normalization
# instead of re-implementing here.
if @map and path of @map
path = @map[path]
parent = parent?split("!")[0]
resolver path, parent
.then (normed) ->
[normed].concat(plugins).join('!')
# TODO: Should probably hook more nicely
System.normalize = normalize
System.normalizeSync = (name) ->
# Seems to fix stuff, no idea why
return name
if typeof module == 'object'
module.exports = monkeypatch
else
monkeypatch(System)