-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.coffee
194 lines (149 loc) · 6.64 KB
/
app.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
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
argv = require('minimist')(process.argv.slice(2))
exec = require('child_process').exec
get_target_content = (sha, repo, target, callback) ->
exec "cd #{repo} && git show #{sha}:#{target} >&1", (error, stdout, stderr) ->
if error or stderr
return callback "Failed to fetch #{sha} from #{repo} for #{target}"
callback null, stdout
parse_target_content = (content) ->
# make a copy of the content before we modify it while parsing
orig_content = content
# first find # of spaces on line of constructor declaration
spaces = (/([\ \t]+)constructor\s*:/.exec content)[1]
# get class name
#class_name = /class (.+)\s/.exec content
method_regex = ///(?:#{spaces}([^:^\s]+):\s*(?:\(([^\)]*)\))?\s*[-=]>)((?:.|[\r\n])*?)(?:\r\n|\n|\r){3,}///
methods = {}
content = content.replace(/\r?\n/g, "\n");
method = method_regex.exec content
while method
entire_method = method[0]
method_name = method[1]
method_param = method[2]
method_param ?= ""
method_param = method_param.split(',')
for value, key in method_param
method_param[key] = value.trim()
method_body = method[3]
methods[method_name] = {param: method_param, body: method_body}
content = content.replace entire_method, ""
method = method_regex.exec content
return methods
parse_class_name = (content) ->
class_regex = /class\s+(\S+)\s+/
(class_regex.exec content)[1]
missing_params = (old_params, new_params) ->
missing = []
for param in new_params
if param not in old_params
missing.push param
return missing
diff_params = (old_params, new_params) ->
if old_params.length isnt new_params.length
return false
else if old_params.join() isnt new_params.join()
return false
else
return true
diff_methods = (old_methods, new_methods) ->
added_methods = {}
removed_methods = {}
changed_methods = {}
unchanged_methods = {}
renamed_methods = {}
for name, old_impl of old_methods
unless new_methods[name]? # method with same name no longer exists
renamed = false
for new_name, new_impl of new_methods # make sure no other methods have same param and body
if (diff_params old_impl.param, new_impl.param) and old_impl.body is new_impl.body
renamed = true
old_impl.new_name = new_name
renamed_methods[name] = old_impl
delete new_methods[new_name]
break
unless renamed
removed_methods[name] = old_impl
else if !(diff_params old_impl.param, new_methods[name].param) # params have changed
changed_methods[name] = {old_method: old_impl, new_method: new_methods[name]}
else
unchanged_methods[name] = new_methods[name] # params and name have not changed
for name, new_impl of new_methods
unless old_methods[name]? # method with same name did not exist previously
added_methods[name] = new_impl
return {added_methods, removed_methods, changed_methods, unchanged_methods, renamed_methods}
generate_adapter = (import_file, methods_diff) ->
#console.log methods_diff
[..., target] = import_file.split('/')
content = "Adaptee = require \"./#{target}\"\n\n"
content += "class Adapter extends Adaptee\n\n"
# handle removed methods
if Object.keys(methods_diff.removed_methods).length > 0
content += " #*** The following methods no longer exist ***\n"
for name, impl of methods_diff.removed_methods
content += " #{name}: (#{impl.param.join(', ')}) ->\n"
content += " # Do nothing when called, or pass args to the newer replacement method\n"
content += " console.log \"Deprecated method #{name} was called\"\n\n"
content += "\n"
# handle changed methods
if Object.keys(methods_diff.changed_methods).length > 0
content += " #*** The following methods have changed interfaces ***\n"
for name, {old_method, new_method} of methods_diff.changed_methods
content += " #{name}: (#{old_method.param.join(', ')}) ->\n"
content += " # Make changes necessary to old input to match new expected input below\n"
for param in (missing_params old_method.param, new_method.param)
content += " #{param} = undefined\n"
content += " super(#{new_method.param.join(', ')})\n\n"
content += "\n"
# handle renamed methods
if Object.keys(methods_diff.renamed_methods).length > 0
content += " #*** The following methods have been renamed ***\n"
for name, impl of methods_diff.renamed_methods
content += " #{name}: (#{impl.param.join(', ')}) ->\n"
content += " # Automatically mapped to new function name\n"
content += " @#{impl.new_name}(#{impl.param.join(', ')})\n\n"
content += "\n"
# handle unchanged methods
if Object.keys(methods_diff.unchanged_methods).length > 0
content += " #*** The following methods have not changed interfaces ***\n"
for name, impl of methods_diff.unchanged_methods
content += " # #{name}: (#{impl.param.join(', ')})\n"
content += "\n\n"
# handle added methods
if Object.keys(methods_diff.added_methods).length > 0
content += " #*** The following methods have been added ***\n"
for name, impl of methods_diff.added_methods
content += " # #{name}: (#{impl.param.join(', ')})\n"
content += "\n\n"
content += "\n\nmodule.exports = Adapter"
main = ->
# Make sure correct arguments have been provided
if (!argv.old or !argv.new or !argv.repo or !argv.target)
console.log "Usage: --repo=[../repo/base/dir/] --target=[relative/to/repo] --old=[OldApiSHA] --new=[NewApiSHA] > adapter.coffee"
return
# Grab old content of target
get_target_content argv.old, argv.repo, argv.target, (err, content) ->
if err
console.log err
return
old_target = content
old_methods = parse_target_content old_target
# Grab new content of target
get_target_content argv.new, argv.repo, argv.target, (err, content) ->
if err
console.log err
return
new_target = content
new_methods = parse_target_content new_target
###
console.log '=================OLD=================='
console.log old_target
console.log '=================NEW=================='
console.log new_target
console.log '=================---=================='
###
# Diff the changes
methods_diff = diff_methods old_methods, new_methods
class_name = parse_class_name new_target
#console.log methods_diff.changed_methods.param_reorder_method
console.log generate_adapter argv.target, methods_diff
main()