UNPKG

@magic/core

Version:

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

234 lines (211 loc) 7.28 kB
import is from '@magic/types' import cases from '@magic/cases' import { handleLink } from '../handleLink.mjs' const isModuleTag = (name, moduleNames) => moduleNames.includes(name) const used = { actions: {}, effects: {}, helpers: {}, lib: {}, tags: new Set(), } // collect all used modules, htmlTags, actions, effects, helpers const findUsedSpells = (t, app, WEB_ROOT) => path => { // find used modules and html tags in app const moduleNames = Object.keys(app.modules) if (t.isCallExpression(path.node)) { if (t.isIdentifier(path.node.callee)) { const { name } = path.node.callee const isModule = isModuleTag(name, moduleNames) if (isModule) { used.tags.add(name) } } else if (t.isMemberExpression(path.node.callee)) { const { callee } = path.node const { key, object } = callee let name if (t.isMemberExpression(callee)) { name = object.name if (object.name === 'lib') { name = callee.property.name } } else if (key) { name = key.name } if (name) { const kebabName = cases.kebab(name) if (Object.keys(app.lib).includes(kebabName)) { if (is.string(app.lib[kebabName])) { used.lib[name] = {} } } } } else { // console.log('unexpected callee type', path.node.callee) } } if (t.isObjectProperty(path.node)) { if (path.node.key) { const validKeys = ['src', 'logo', 'href', 'to'] if (t.isIdentifier(path.node.key)) { if (path.node.value.value) { if (validKeys.includes(path.node.key.name)) { path.node.value.value = handleLink({ href: path.node.value.value, app, WEB_ROOT }) } } } else if (t.isStringLiteral(path.node.key)) { const { value: name } = path.node.key if (validKeys.includes(name)) { path.node.value.value = handleLink({ href: path.node.value.value, app, WEB_ROOT }) } } } } else if (t.isArrayExpression(path.node)) { const [action, helper] = path.node.elements if (action) { if (t.isMemberExpression(action)) { const { name: type } = action.object if (used[type]) { if (t.isMemberExpression(action.object)) { const { name: objName } = action.object.object const { name: propName } = action.object.property // console.log({ objName, propName }) if (type !== objName) { used[type][objName] = used[type][objName] || {} used[type][objName][propName] = {} } else { used[type] = used[type] || {} used[type][propName] = {} } } else if (t.isIdentifier(action.object)) { const { name: objName } = action.object const { name: propName } = action.property if (objName !== type) { used[type][objName] = used[type][objName] || {} used[type][objName][propName] = {} } else { used[type] = used[type] || {} used[type][propName] = {} } } } } if (t.isMemberExpression(helper)) { const { name: type } = helper.object if (used.hasOwnProperty(type)) { if (t.isMemberExpression(helper.object)) { const { name: objName } = helper.object.object const { name: propName } = helper.object.property used[type][objName] = used[type][objName] || {} used[type][objName][propName] = {} } else if (t.isIdentifier(helper.object)) { const { name: objName } = helper.object const { name: propName } = helper.property if (objName !== type) { used[type][objName] = used[type][objName] || {} used[type][objName][propName] = {} } else { used[type] = used[type] || {} used[type][propName] = {} } } } } // } else if (t.isAssignmentExpression(path.node)) { // const { left, right } = path.node // let objName // let propName // if (t.isMemberExpression(left)) { // // props.onclick = action // // console.log('member expression', left) // objName = left.object.name // propName = left.property.name // } else if (t.isIdentifier(left)) { // // props = { onclick: action } // objName = left.name // } // console.log({ objName, propName }) } } } const removeUnused = (t, app) => path => { const moduleNames = Object.keys(app.modules) const usedTagNames = Array.from(used.tags) if (t.isVariableDeclarator(path.node)) { const { name } = path.node.id if (name && isModuleTag(name, moduleNames)) { if (!usedTagNames.includes(name)) { if (isModuleTag(name, moduleNames)) { if (!is.case.upper(name[0])) { const init = path.node.init if (!init) { return } const callee = init.callee if (!callee || callee.name !== 'C') { return } // console.log('removed html tag', name) path.remove() } else { // console.log('removed @magic-module', name) path.remove() } } } } else { if (name === 'lib') { // remove unused lib imports // if (path.node.init && path.node.init.properties) { // const { properties } = path.node.init // properties.forEach(prop => { // console.log(prop.key.name, Object.keys(used.lib).includes(prop.key.name)) // }) // console.log(used.lib); // } } if (name === 'actions') { // remove unused actions path.node.init.properties.forEach(prop => { const { value: name } = prop.key // console.log({ actions: used.actions }) if (t.isFunctionExpression(prop.value)) { if (!used.actions || !used.actions.hasOwnProperty(name)) { // console.log('remove fn', name) // prop.remove() } else { // console.log('not remove fn', name) } } else if (t.isObjectExpression(prop.value)) { const { value: propName } = prop.value.properties[0].key if ( !used.actions || !used.actions[name] || !used.actions[name].hasOwnProperty(propName) ) { // console.log('remove actions', name, propName) // prop.remove() } else { // console.log('not remove action', name, propName) } } else { // console.log('unexpected action type', prop) } }) } } } } export default (app, { WEB_ROOT }) => ({ types: t }) => ({ visitor: { Program: { enter(path) { path.traverse({ enter: findUsedSpells(t, app, WEB_ROOT) }) }, exit(path) { path.traverse({ enter: removeUnused(t, app) }) }, }, }, })