Allow to output either raw or transformed AST, fix several transformation bugs with...
[jst.git] / transform.js
1 /*
2  * Copyright (C) 2022 Nick Downing <nick@ndcode.org>
3  * SPDX-License-Identifier: MIT
4  * 
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  * 
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  * 
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  */
23
24 let transform = (visitors, node, state) => {
25   let c = (node, st, override) =>
26     visitors[override || node.type](node, st, c)
27   return c(node, state, node.type)
28 }
29
30 // remainder of the file is adapted from acorn-walk/src/index.js
31 // we have changed "base" to "visitors"
32 // we have made tree be reconstructed by replacing each child element with
33 // the return value of its walker (and most walkers will return themselves)
34
35 let skipThrough = (node, st, c) => c(node, st)
36 let ignore = (node, st, c) => node
37
38
39 // Node walkers.
40 let visitors = {}
41
42 visitors.Program =
43 visitors.BlockStatement =
44 visitors.StaticBlock = (node, st, c) => {
45   for (let i = 0; i < node.body.length; ++i)
46     node.body[i] = c(node.body[i], st, 'Statement')
47   return node
48 }
49 visitors.Statement = skipThrough
50 visitors.EmptyStatement = ignore
51 visitors.ExpressionStatement =
52 visitors.ParenthesizedExpression =
53 visitors.ChainExpression = (node, st, c) => {
54   node.expression = c(node.expression, st, 'Expression')
55   return node
56 }
57 visitors.IfStatement = (node, st, c) => {
58   node.test = c(node.test, st, 'Expression')
59   node.consequent = c(node.consequent, st, 'Statement')
60   if (node.alternate) node.alternate = c(node.alternate, st, 'Statement')
61   return node
62 }
63 visitors.LabeledStatement = (node, st, c) => {
64   node.body = c(node.body, st, 'Statement')
65   return node
66 }
67 visitors.BreakStatement =
68 visitors.ContinueStatement = ignore
69 visitors.WithStatement = (node, st, c) => {
70   node.object = c(node.object, st, 'Expression')
71   node.body = c(node.body, st, 'Statement')
72   return node
73 }
74 visitors.SwitchStatement = (node, st, c) => {
75   node.discriminant = c(node.discriminant, st, 'Expression')
76   for (let cs of node.cases) {
77     if (cs.test) cs.test = c(cs.test, st, 'Expression')
78     for (let i = 0; i < cs.consequent.length; ++i)
79       cs.consequent[i] = c(cs.consequent[i], st, 'Statement')
80   }
81   return node
82 }
83 visitors.SwitchCase = (node, st, c) => {
84   if (node.test) node.test = c(node.test, st, 'Expression')
85   for (let i = 0; i < node.consequent.length; ++i)
86     node.consequent[i] = c(node.consequent[i], st, 'Statement')
87   return node
88 }
89 visitors.ReturnStatement =
90 visitors.YieldExpression =
91 visitors.AwaitExpression =
92 visitors.InterpolateExpression = (node, st, c) => {
93   if (node.argument) node.argument = c(node.argument, st, 'Expression')
94   return node
95 }
96 visitors.ThrowStatement =
97 visitors.SpreadElement = (node, st, c) => {
98   node.argument = c(node.argument, st, 'Expression')
99   return node
100 }
101 visitors.TryStatement = (node, st, c) => {
102   node.block = c(node.block, st, 'Statement')
103   if (node.handler) node.handler = c(node.handler, st)
104   if (node.finalizer) node.finalizer = c(node.finalizer, st, 'Statement')
105   return node
106 }
107 visitors.CatchClause = (node, st, c) => {
108   if (node.param) node.param = c(node.param, st, 'Pattern')
109   node.body = c(node.body, st, 'Statement')
110   return node
111 }
112 visitors.WhileStatement =
113 visitors.DoWhileStatement = (node, st, c) => {
114   node.test = c(node.test, st, 'Expression')
115   node.body = c(node.body, st, 'Statement')
116   return node
117 }
118 visitors.ForStatement = (node, st, c) => {
119   if (node.init) node.init = c(node.init, st, 'ForInit')
120   if (node.test) node.test = c(node.test, st, 'Expression')
121   if (node.update) node.update = c(node.update, st, 'Expression')
122   node.body = c(node.body, st, 'Statement')
123   return node
124 }
125 visitors.ForInStatement =
126 visitors.ForOfStatement = (node, st, c) => {
127   node.left = c(node.left, st, 'ForInit')
128   node.right = c(node.right, st, 'Expression')
129   node.body = c(node.body, st, 'Statement')
130   return node
131 }
132 visitors.ForInit = (node, st, c) => {
133   if (node.type === 'VariableDeclaration') return c(node, st)
134   else return c(node, st, 'Expression')
135   return node
136 }
137 visitors.DebuggerStatement = ignore
138
139 visitors.FunctionDeclaration = (node, st, c) => c(node, st, 'Function')
140 visitors.VariableDeclaration = (node, st, c) => {
141   for (let i = 0; i < node.declarations.length; ++i)
142     node.declarations[i] = c(node.declarations[i], st)
143   return node
144 }
145 visitors.VariableDeclarator = (node, st, c) => {
146   node.id = c(node.id, st, 'Pattern')
147   if (node.init) node.init = c(node.init, st, 'Expression')
148   return node
149 }
150
151 visitors.Function = (node, st, c) => {
152   if (node.id) node.id = c(node.id, st, 'Pattern')
153   for (let i = 0; i < node.params.length; ++i)
154     node.params[i] = c(node.params[i], st, 'Pattern')
155   node.body = c(node.body, st, node.expression ? 'Expression' : 'Statement')
156   return node
157 }
158
159 visitors.Pattern = (node, st, c) => {
160   if (node.type === 'Identifier')
161     return c(node, st, 'VariablePattern')
162   else if (node.type === 'MemberExpression')
163     return c(node, st, 'MemberPattern')
164   else
165     return c(node, st)
166   return node
167 }
168 visitors.VariablePattern = ignore
169 visitors.MemberPattern = skipThrough
170 visitors.RestElement = (node, st, c) => {
171   node.argument = c(node.argument, st, 'Pattern')
172   return node
173 }
174 visitors.ArrayPattern = (node, st, c) => {
175   for (let i = 0; i < node.elements.length; ++i) {
176     let elt = node.elements[i]
177     if (elt) node.elements[i] = c(elt, st, 'Pattern')
178   }
179   return node
180 }
181 visitors.ObjectPattern = (node, st, c) => {
182   for (let prop of node.properties) {
183     if (prop.type === 'Property') {
184       if (prop.computed) prop.key = c(prop.key, st, 'Expression')
185       prop.value = c(prop.value, st, 'Pattern')
186     } else if (prop.type === 'RestElement') {
187       prop.argument = c(prop.argument, st, 'Pattern')
188     }
189   }
190   return node
191 }
192
193 visitors.Expression = skipThrough
194 visitors.ThisExpression =
195 visitors.Super =
196 visitors.MetaProperty = ignore
197 visitors.ArrayExpression = (node, st, c) => {
198   for (let i = 0; i < node.elements.length; ++i) {
199     let elt = node.elements[i]
200     if (elt) node.elements[i] = c(elt, st, 'Expression')
201   }
202   return node
203 }
204 visitors.ObjectExpression = (node, st, c) => {
205   for (let i = 0; i < node.properties.length; ++i)
206     node.properties[i] = c(node.properties[i], st)
207   return node
208 }
209 visitors.FunctionExpression =
210 visitors.ArrowFunctionExpression = visitors.FunctionDeclaration
211 visitors.SequenceExpression = (node, st, c) => {
212   for (let i = 0; i < node.expressions.length; ++i)
213     node.expressions[i] = c(node.expressions[i], st, 'Expression')
214   return node
215 }
216 visitors.TemplateLiteral = (node, st, c) => {
217   for (let i = 0; i < node.quasis.length; ++i)
218     node.quasis[i] = c(node.quasis[i], st)
219
220   for (let i = 0; i < node.expressions.length; ++i)
221     node.expressions[i] = c(node.expressions[i], st, 'Expression')
222   return node
223 }
224 visitors.TemplateElement = ignore
225 visitors.UnaryExpression =
226 visitors.UpdateExpression = (node, st, c) => {
227   node.argument = c(node.argument, st, 'Expression')
228   return node
229 }
230 visitors.BinaryExpression =
231 visitors.LogicalExpression = (node, st, c) => {
232   node.left = c(node.left, st, 'Expression')
233   node.right = c(node.right, st, 'Expression')
234   return node
235 }
236 visitors.AssignmentExpression =
237 visitors.AssignmentPattern = (node, st, c) => {
238   node.left = c(node.left, st, 'Pattern')
239   node.right = c(node.right, st, 'Expression')
240   return node
241 }
242 visitors.ConditionalExpression = (node, st, c) => {
243   node.test = c(node.test, st, 'Expression')
244   node.consequent = c(node.consequent, st, 'Expression')
245   node.alternate = c(node.alternate, st, 'Expression')
246   return node
247 }
248 visitors.NewExpression =
249 visitors.CallExpression = (node, st, c) => {
250   node.callee = c(node.callee, st, 'Expression')
251   if (node.arguments)
252     for (let i = 0; i < node.arguments.length; ++i)
253       node.arguments[i] = c(node.arguments[i], st, 'Expression')
254   return node
255 }
256 visitors.MemberExpression = (node, st, c) => {
257   node.object = c(node.object, st, 'Expression')
258   if (node.computed) node.property = c(node.property, st, 'Expression')
259   return node
260 }
261 visitors.ExportNamedDeclaration =
262 visitors.ExportDefaultDeclaration = (node, st, c) => {
263   if (node.declaration)
264     node.declaration = c(node.declaration, st, node.type === 'ExportNamedDeclaration' || node.declaration.id ? 'Statement' : 'Expression')
265   if (node.source) node.source = c(node.source, st, 'Expression')
266   return node
267 }
268 visitors.ExportAllDeclaration = (node, st, c) => {
269   if (node.exported)
270     node.exported = c(node.exported, st)
271   node.source = c(node.source, st, 'Expression')
272   return node
273 }
274 visitors.ImportDeclaration = (node, st, c) => {
275   for (let i = 0; i < node.specifiers.length; ++i)
276     node.specifiers[i] = c(node.specifiers[i], st)
277   node.source = c(node.source, st, 'Expression')
278   return node
279 }
280 visitors.ImportExpression = (node, st, c) => {
281   node.source = c(node.source, st, "Expression")
282   return node
283 }
284 visitors.ImportSpecifier =
285 visitors.ImportDefaultSpecifier =
286 visitors.ImportNamespaceSpecifier =
287 visitors.Identifier =
288 visitors.PrivateIdentifier =
289 visitors.Literal = ignore
290
291 visitors.TaggedTemplateExpression = (node, st, c) => {
292   node.tag = c(node.tag, st, 'Expression')
293   node.quasi = c(node.quasi, st, 'Expression')
294   return node
295 }
296 visitors.ClassDeclaration =
297 visitors.ClassExpression = (node, st, c) => c(node, st, 'Class')
298 visitors.Class = (node, st, c) => {
299   if (node.id) node.id = c(node.id, st, 'Pattern')
300   if (node.superClass) node.superClass = c(node.superClass, st, 'Expression')
301   node.body = c(node.body, st)
302   return node
303 }
304 visitors.ClassBody = (node, st, c) => {
305   for (let i = 0; i < node.body.length; ++i)
306     node.body[i] = c(node.body[i], st)
307   return node
308 }
309 visitors.MethodDefinition =
310 visitors.PropertyDefinition =
311 visitors.Property = (node, st, c) => {
312   if (node.computed) node.key = c(node.key, st, 'Expression')
313   if (node.value) node.value = c(node.value, st, 'Expression')
314   return node
315 }
316
317 // Nick
318 visitors.HTMLExpression = (node, st, c) => {
319   node.tag = c(node.tag, st, 'Expression')
320   node.body = c(node.body, st, 'Statement')
321   return node
322 }
323
324 exports.transform = transform
325 exports.visitors = visitors