Skip to content

Commit 42100ba

Browse files
committed
Improve and refactor bin/moonc code into moonscript.cmd.moonc
Benefits and changes: - Implemented the rsync-like exclusive/inclusive directory behaviour brought up in issue #342 - Unified CLI file/directory input handling across watch and non-watch code paths - Everything works OK with absolute paths now, although I can't guarantee that it won't violate some user's expectations -- but the behaviour is all consistent - By converting moonc to Moonscript and breaking it up into smaller functions, it should be easier to read, and its component parts could also now be covered by the Busted tests - Also replaces the existing test filesystem stubs/mocks with some more expansive ones, in preparation for increasing test coverage
1 parent e1acafc commit 42100ba

File tree

8 files changed

+1152
-327
lines changed

8 files changed

+1152
-327
lines changed

bin/moonc

Lines changed: 2 additions & 230 deletions
Original file line numberDiff line numberDiff line change
@@ -1,232 +1,4 @@
11
#!/usr/bin/env lua
22

3-
local argparse = require "argparse"
4-
local lfs = require "lfs"
5-
6-
local parser = argparse()
7-
8-
parser:flag("-l --lint", "Perform a lint on the file instead of compiling")
9-
parser:flag("-v --version", "Print version")
10-
parser:flag("-w --watch", "Watch file/directory for updates")
11-
parser:option("--transform", "Transform syntax tree with module")
12-
parser:mutex(
13-
parser:option("-t --output-to", "Specify where to place compiled files"),
14-
parser:option("-o", "Write output to file"),
15-
parser:flag("-p", "Write output to standard output"),
16-
parser:flag("-T", "Write parse tree instead of code (to stdout)"),
17-
parser:flag("-b", "Write parse and compile time instead of code(to stdout)"),
18-
parser:flag("-X", "Write line rewrite map instead of code (to stdout)")
19-
)
20-
parser:flag("-",
21-
"Read from standard in, print to standard out (Must be only argument)")
22-
23-
local read_stdin = arg[1] == "--" -- luacheck: ignore 113
24-
25-
if not read_stdin then
26-
parser:argument("file/directory"):args("+")
27-
end
28-
29-
local opts = parser:parse()
30-
31-
if opts.version then
32-
local v = require "moonscript.version"
33-
v.print_version()
34-
os.exit()
35-
end
36-
37-
function log_msg(...)
38-
if not opts.p then
39-
io.stderr:write(table.concat({...}, " ") .. "\n")
40-
end
41-
end
42-
43-
local moonc = require("moonscript.cmd.moonc")
44-
local util = require "moonscript.util"
45-
local normalize_dir = moonc.normalize_dir
46-
local compile_and_write = moonc.compile_and_write
47-
local path_to_target = moonc.path_to_target
48-
49-
local function scan_directory(root, collected)
50-
root = normalize_dir(root)
51-
collected = collected or {}
52-
53-
for fname in lfs.dir(root) do
54-
if not fname:match("^%.") then
55-
local full_path = root..fname
56-
57-
if lfs.attributes(full_path, "mode") == "directory" then
58-
scan_directory(full_path, collected)
59-
elseif fname:match("%.moon$") then
60-
table.insert(collected, full_path)
61-
end
62-
end
63-
end
64-
65-
return collected
66-
end
67-
68-
local function remove_dups(tbl, key_fn)
69-
local hash = {}
70-
local final = {}
71-
72-
for _, v in ipairs(tbl) do
73-
local dup_key = key_fn and key_fn(v) or v
74-
if not hash[dup_key] then
75-
table.insert(final, v)
76-
hash[dup_key] = true
77-
end
78-
end
79-
80-
return final
81-
end
82-
83-
-- creates tuples of input and target
84-
local function get_files(fname, files)
85-
files = files or {}
86-
87-
if lfs.attributes(fname, "mode") == "directory" then
88-
for _, sub_fname in ipairs(scan_directory(fname)) do
89-
table.insert(files, {
90-
sub_fname,
91-
path_to_target(sub_fname, opts.output_to, fname)
92-
})
93-
end
94-
else
95-
table.insert(files, {
96-
fname,
97-
path_to_target(fname, opts.output_to)
98-
})
99-
end
100-
101-
return files
102-
end
103-
104-
if read_stdin then
105-
local parse = require "moonscript.parse"
106-
local compile = require "moonscript.compile"
107-
108-
local text = io.stdin:read("*a")
109-
local tree, err = parse.string(text)
110-
111-
if not tree then error(err) end
112-
local code, err, pos = compile.tree(tree)
113-
114-
if not code then
115-
error(compile.format_error(err, pos, text))
116-
end
117-
118-
print(code)
119-
os.exit()
120-
end
121-
122-
local inputs = opts["file/directory"]
123-
124-
local files = {}
125-
for _, input in ipairs(inputs) do
126-
get_files(input, files)
127-
end
128-
129-
files = remove_dups(files, function(f)
130-
return f[2]
131-
end)
132-
133-
-- returns an iterator that returns files that have been updated
134-
local function create_watcher(files)
135-
local watchers = require("moonscript.cmd.watchers")
136-
137-
if watchers.InotifyWacher:available() then
138-
return watchers.InotifyWacher(files):each_update()
139-
end
140-
141-
return watchers.SleepWatcher(files):each_update()
142-
end
143-
144-
if opts.watch then
145-
-- build function to check for lint or compile in watch
146-
local handle_file
147-
if opts.lint then
148-
local lint = require "moonscript.cmd.lint"
149-
handle_file = lint.lint_file
150-
else
151-
handle_file = compile_and_write
152-
end
153-
154-
local watcher = create_watcher(files)
155-
-- catches interrupt error for ctl-c
156-
local protected = function()
157-
local status, file = true, watcher()
158-
if status then
159-
return file
160-
elseif file ~= "interrupted!" then
161-
error(file)
162-
end
163-
end
164-
165-
for fname in protected do
166-
local target = path_to_target(fname, opts.output_to)
167-
168-
if opts.o then
169-
target = opts.o
170-
end
171-
172-
local success, err = handle_file(fname, target)
173-
if opts.lint then
174-
if success then
175-
io.stderr:write(success .. "\n\n")
176-
elseif err then
177-
io.stderr:write(fname .. "\n" .. err .. "\n\n")
178-
end
179-
elseif not success then
180-
io.stderr:write(table.concat({
181-
"",
182-
"Error: " .. fname,
183-
err,
184-
"\n",
185-
}, "\n"))
186-
elseif success == "build" then
187-
log_msg("Built", fname, "->", target)
188-
end
189-
end
190-
191-
io.stderr:write("\nQuitting...\n")
192-
elseif opts.lint then
193-
local has_linted_with_error;
194-
local lint = require "moonscript.cmd.lint"
195-
for _, tuple in pairs(files) do
196-
local fname = tuple[1]
197-
local res, err = lint.lint_file(fname)
198-
if res then
199-
has_linted_with_error = true
200-
io.stderr:write(res .. "\n\n")
201-
elseif err then
202-
has_linted_with_error = true
203-
io.stderr:write(fname .. "\n" .. err.. "\n\n")
204-
end
205-
end
206-
if has_linted_with_error then
207-
os.exit(1)
208-
end
209-
else
210-
for _, tuple in ipairs(files) do
211-
local fname, target = util.unpack(tuple)
212-
if opts.o then
213-
target = opts.o
214-
end
215-
216-
local success, err = compile_and_write(fname, target, {
217-
print = opts.p,
218-
fname = fname,
219-
benchmark = opts.b,
220-
show_posmap = opts.X,
221-
show_parse_tree = opts.T,
222-
transform_module = opts.transform
223-
})
224-
225-
if not success then
226-
io.stderr:write(fname .. "\t" .. err .. "\n")
227-
os.exit(1)
228-
end
229-
end
230-
end
231-
232-
3+
local moonc = require "moonscript.cmd.moonc"
4+
moonc.main(arg)

0 commit comments

Comments
 (0)