-
Notifications
You must be signed in to change notification settings - Fork 83
/
npm.lua
242 lines (207 loc) · 6.62 KB
/
npm.lua
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
-- preamble: common routines
local JSON = require("JSON")
-- silence JSON parsing errors
function JSON:assert () end -- luacheck: no unused args
local color = require('color')
local w = require('tables').wrap
local matchers = require('matchers')
---
-- Queries config options value using 'npm config' call
-- @param {string} config_entry Config option name
-- @return {string} Config value for specific option or
-- empty string in case of any error
---
local function get_npm_config_value (config_entry)
assert(config_entry and type(config_entry) == "string" and #config_entry > 0,
"get_npm_config_value: config_entry param should be non-empty string")
local proc = io.popen("2>nul npm config get "..config_entry)
if not proc then return "" end
local value = proc:read()
proc:close()
return value or nil
end
local modules = matchers.create_dirs_matcher('node_modules/*')
local cache_location = nil
local cached_modules_matcher = nil
local function cached_modules(token)
-- If we already have matcher then just return it
if cached_modules_matcher then return cached_modules_matcher(token) end
-- otherwise try to get cache location and return empty table if failed
cache_location = cache_location or get_npm_config_value("cache")
if not cache_location then return {} end
-- Create a new matcher, save it in module's variable for further usage and return it
cached_modules_matcher = matchers.create_dirs_matcher(cache_location..'/*')
return cached_modules_matcher(token)
end
local globals_location = nil
local global_modules_matcher = nil
local function global_modules(token)
-- If we already have matcher then just return it
if global_modules_matcher then return global_modules_matcher(token) end
-- If token starts with . or .. or has path delimiter then return empty
-- result and do not create a matcher so only fs paths will be completed
if (token:match('^%.(%.)?') or token:match('[%\\%/]+')) then return {} end
-- otherwise try to get cache location and return empty table if failed
globals_location = globals_location or get_npm_config_value("prefix")
if not globals_location then return {} end
-- Create a new matcher, save it in module's variable for further usage and return it
global_modules_matcher = matchers.create_dirs_matcher(globals_location..'/node_modules/*')
return global_modules_matcher(token)
end
-- Reads package.json in current directory and extracts all "script" commands defined
local function scripts(token) -- luacheck: no unused args
-- Read package.json first
local package_json = io.open('package.json')
-- If there is no such file, then close handle and return
if package_json == nil then return w() end
-- Read the whole file contents
local package_contents = package_json:read("*a")
package_json:close()
local package_scripts = JSON:decode(package_contents).scripts
return w(package_scripts):keys()
end
local parser = clink.arg.new_parser
-- end preamble
local install_parser = parser({matchers.dirs},
"--force",
"-g", "--global",
"--link",
"--no-bin-links",
"--no-optional",
"--no-shrinkwrap",
"--nodedir=/",
"--production",
"--save", "--save-dev", "--save-optional",
"--tag"
):loop(1)
-- TODO: list only global modules with -g
local remove_parser = parser({modules}, "-g", "--global"):loop(1)
local search_parser = parser("--long")
local script_parser = parser({scripts})
local list_parser = parser(
{modules},
"--prod", "--production",
"--dev", "--development",
"--only"..parser({"dev", "prod"}),
"--json",
"--long",
"--parseable",
"--global", "-g",
"--depth",
"--link"
)
local npm_parser = parser({
"add-user",
"adduser",
"apihelp",
"audit"..parser({
"fix"..parser("--force", "--package-lock-only", "--dry-run", "--production", "--only=dev"),
"--json",
"--parseable"
}),
"author",
"bin",
"bugs",
"c",
"cache"..parser({
"add"..parser({matchers.dirs}),
"clean"..parser({cached_modules}),
"ls"
}),
"completion",
"config",
"ddp",
"dedupe",
"deprecate",
"docs",
"edit",
"explore",
"faq",
"find" .. search_parser,
"find-dupes",
"get",
"help",
"help-search",
"home",
"info",
"init",
"install" .. install_parser,
"issues",
"la",
"link"..parser({matchers.files, global_modules}),
"list"..list_parser,
"ll"..list_parser,
"ln"..parser({matchers.files, global_modules}),
"login",
"ls"..list_parser,
"outdated"..parser(
"--json",
"--long",
"--parseable",
"--global",
"--depth"
),
"owner",
"pack",
"prefix",
"prune",
"publish"..parser(
"--tag",
"--access"..parser({"public", "restricted"})
),
"r",
"rb",
"rebuild",
"rm" .. remove_parser,
"remove" .. remove_parser,
"repo",
"restart",
"root",
"run"..script_parser,
"run-script"..script_parser,
"search" .. search_parser,
"set",
"show",
"shrinkwrap",
"star",
"stars",
"start",
"stop",
"submodule",
"tag",
"test",
"un",
"uninstall" .. remove_parser,
"unlink",
"unpublish",
"unstar",
"up"..parser({modules}),
"update"..parser({modules}),
"v",
"version",
"view",
"whoami"
},
"-h", "--version"
)
clink.arg.register_parser("npm", npm_parser)
local function npm_prompt_filter()
-- Automatically disable this when a .clinkprompt custom prompt is active.
local customprompt = clink.getclinkprompt and clink.getclinkprompt()
if customprompt and customprompt ~= "" then return false end
local package_file = io.open('package.json')
if not package_file then return false end
local package_data = package_file:read('*a')
package_file:close()
local package = JSON:decode(package_data)
-- Bail out if package.json is malformed
if not package then return false end
-- Don't print package info when the package is private or both version and name are missing
if package.private or (not package.name and not package.version) then return false end
local package_name = package.name or "<no name>"
local package_version = package.version and "@"..package.version or ""
local package_string = color.color_text("("..package_name..package_version..")", color.YELLOW)
clink.prompt.value = clink.prompt.value:gsub('{git}', '{git} '..package_string)
return false
end
clink.prompt.register_filter(npm_prompt_filter, 40)