UNPKG

@magic/core

Version:

@magic core. generate static pages and serverless lambdas. ~5kb client boilerplate.

201 lines (186 loc) 8.81 kB
import log from '@magic/log' const noopTypes = [ 'StringLiteral', 'BooleanLiteral', 'NullLiteral', 'NumericLiteral', 'RegExpLiteral', 'ThisExpression', 'EmptyStatement', 'Identifier', ] /* * which types of member accesses we want to track in the used object */ const validTypes = ['lib', 'actions', 'effects', 'subscriptions', 'helpers'] const visit = ({ parent, app, used }) => { if (!parent) { return parent } if (Array.isArray(parent)) { return parent.map(n => visit({ parent: n, app, used })) } if (parent.type === 'VariableDeclaration') { parent.declarations = parent.declarations.map(decl => visit({ parent: decl, app, used })) } else if (parent.type === 'ExpressionStatement') { parent.expression = visit({ parent: parent.expression, app, used }) } else if (parent.type === 'AssignmentExpression') { parent.right = visit({ parent: parent.right, app, used }) parent.left = visit({ parent: parent.left, app, used }) } else if (parent.type === 'ArrowFunctionExpression') { if (parent.params) { parent.params = visit({ parent: parent.params, app, used }) parent.body = visit({ parent: parent.body, app, used }) } else if (parent.body.callee) { parent.body.callee = visit({ parent: parent.body.callee, app, used }) parent.params = visit({ parent: parent.params, app, used }) } else if (parent.body.stmts) { parent.body.stmts = parent.body.stmts.map(stmt => visit({ parent: stmt, app, used })) } else { log.warn('unhandled ArrowFunctionExpression', parent) } } else if (parent.type === 'IfStatement') { parent.test = visit({ parent: parent.test, app, used }) parent.consequent = visit({ parent: parent.consequent, app, used }) if (parent.alternate) { parent.alternate = visit({ parent: parent.alternate, app, used }) } } else if (parent.type === 'CallExpression') { parent.arguments = parent.arguments.map(arg => { arg.expression = visit({ parent: arg.expression, app, used }) return arg }) parent.callee = visit({ parent: parent.callee, app, used }) if (parent.callee.type === 'Identifier') { if (Object.keys(app.modules).includes(parent.callee.value)) { if (!used.modules.includes(parent.callee.value)) { used.modules.push(parent.callee.value) } } } } else if (parent.type === 'VariableDeclarator') { parent.id = visit({ parent: parent.id, app, used }) parent.init = visit({ parent: parent.init, app, used }) } else if (parent.type === 'ObjectExpression') { parent.properties = parent.properties.map(prop => visit({ parent: prop, app, used })) } else if (parent.type === 'KeyValueProperty') { parent.key = visit({ parent: parent.key, app, used }) parent.value = visit({ parent: parent.value, app, used }) } else if (parent.type === 'MemberExpression') { parent.property = visit({ parent: parent.property, app, used }) parent.object = visit({ parent: parent.object, app, used }) const { value: type } = parent.object const { value: key } = parent.property if (used.hasOwnProperty(type) && !used[type].includes(key)) { used[type].push(key) } if (Object.keys(app.modules).includes(type) && type[0].toUpperCase() === type[0]) { used.modules.push(`${type}.${key}`) } } else if (parent.type === 'ArrayExpression') { parent.elements = parent.elements.map(ele => { ele.expression = visit({ parent: ele.expression, undefined, app, used }) return ele }) } else if (parent.type === 'ArrayPattern') { parent.elements = parent.elements.map(ele => visit({ parent: ele, app, used })) } else if (parent.type === 'TemplateLiteral') { parent.quasis = parent.quasis.map(quasi => { quasi.raw = visit({ parent: quasi.raw, app, used }) quasi.cooked = visit({ parent: quasi.cooked, app, used }) return quasi }) } else if (parent.type === 'SpreadElement') { parent.arguments = visit({ parent: parent.arguments, app, used }) } else if (parent.type === 'BlockStatement') { parent.stmts = parent.stmts.map(stmt => visit({ parent: stmt, app, used })) } else if (parent.type === 'ReturnStatement') { parent.argument = visit({ parent: parent.argument, app, used }) } else if (parent.type === 'ObjectPattern') { parent.properties.map(prop => visit({ parent: prop, app, used })) } else if (parent.type === 'AssignmentPattern') { parent.left = visit({ parent: parent.left, app, used }) parent.right = visit({ parent: parent.right, app, used }) } else if (parent.type === 'AssignmentPatternProperty') { parent.key = visit({ parent: parent.key, app, used }) } else if (parent.type === 'ConditionalExpression') { parent.test = visit({ parent: parent.test, app, used }) parent.consequent = visit({ parent: parent.consequent, app, used }) parent.alternate = visit({ parent: parent.alternate, app, used }) } else if (parent.type === 'BinaryExpression') { parent.left = visit({ parent: parent.left, app, used }) parent.right = visit({ parent: parent.right, app, used }) } else if (parent.type === 'UnaryExpression') { parent.argument = visit({ parent: parent.argument, app, used }) } else if (parent.type === 'NewExpression') { parent.callee = visit({ parent: parent.callee, app, used }) } else if (parent.type === 'ParenthesisExpression') { parent.expression = visit({ parent: parent.expression, app, used }) } else if (parent.type === 'RestElement') { parent.argument = visit({ parent: parent.argument, undefined, app, used }) } else if (parent.type === 'KeyValuePatternProperty') { parent.key = visit({ parent: parent.key, app, used }) parent.value = visit({ parent: parent.value, app, used }) } else if (parent.type === 'TryStatement') { parent.block = visit({ parent: parent.block, app, used }) parent.handler = visit({ parent: parent.handler, app, used }) } else if (parent.type === 'CatchClause') { parent.param = visit({ parent: parent.param, app, used }) parent.body = visit({ parent: parent.body, app, used }) } else if (parent.type === 'Computed') { parent.expression = visit({ parent: parent.expression, app, used }) } else if (parent.type === 'OptionalChainingExpression') { parent.expr = visit({ parent: parent.expr, app, used }) } else if (parent.type === 'ForStatement') { parent.init = visit({ parent: parent.init, app, used }) parent.test = visit({ parent: parent.test, app, used }) parent.update = visit({ parent: parent.update, app, used }) parent.body = visit({ parent: parent.body, app, used }) } else if (parent.type === 'FunctionExpression') { parent.params = visit({ parent: parent.params, app, used }) parent.body = visit({ parent: parent.body, app, used }) parent.decorators = visit({ parent: parent.decorators, app, used }) } else if (parent.type === 'Parameter') { parent.pat = visit({ parent: parent.pat, app, used }) } else if (parent.type === 'ForInStatement') { parent.left = visit({ parent: parent.left, app, used }) parent.right = visit({ parent: parent.right, app, used }) parent.body = visit({ parent: parent.body, app, used }) } else if (parent.type === 'UpdateExpression') { parent.argument = visit({ parent: parent.argument, app, used }) } else if (parent.type === 'WhileStatement') { parent.test = visit({ parent: parent.test, app, used }) parent.body = visit({ parent: parent.body, app, used }) } else if (parent.type === 'BreakStatement') { parent.label = visit({ parent: parent.label, app, used }) } else if (parent.type === 'SequenceExpression') { parent.expressions = visit({ parent: parent.expressions, app, used }) } else if (parent.type === 'ContinueStatement') { parent.label = visit({ parent: parent.label, app, used }) } else if (parent.type === 'ImportDeclaration') { parent.specifiers = visit({ parent: parent.specifiers, app, used }) } else if (parent.type === 'ImportSpecifier') { parent.local = visit({ parent: parent.local, app, used }) } else if (parent.type === 'ExportNamedDeclaration') { parent.specifiers = visit({ parent: parent.specifiers, app, used }) } else if (parent.type === 'ExportSpecifier') { parent.orig = visit({ parent: parent.orig, app, used }) } else if (noopTypes.includes(parent.type)) { // noop } else if (parent.type) { log.warn('unexpected parent type', parent.type, parent) } return parent } export const resolveDependencies = ({ parent: m, app }) => { const used = { modules: [], lib: [], actions: [], effects: [], subscriptions: [], helpers: [], } m.body.map(item => visit({ parent: item, app, used })) return used }