2020-11-06 03:33:14 +01:00
|
|
|
import {
|
|
|
|
NodePath,
|
|
|
|
PluginObj,
|
|
|
|
types as BabelTypes,
|
|
|
|
} from 'next/dist/compiled/babel/core'
|
2019-08-16 00:08:06 +02:00
|
|
|
|
|
|
|
// matches any hook-like (the default)
|
|
|
|
const isHook = /^use[A-Z]/
|
|
|
|
|
|
|
|
// matches only built-in hooks provided by React et al
|
|
|
|
const isBuiltInHook = /^use(Callback|Context|DebugValue|Effect|ImperativeHandle|LayoutEffect|Memo|Reducer|Ref|State)$/
|
|
|
|
|
2020-05-18 21:24:37 +02:00
|
|
|
export default function ({
|
2019-08-16 00:08:06 +02:00
|
|
|
types: t,
|
|
|
|
}: {
|
|
|
|
types: typeof BabelTypes
|
|
|
|
}): PluginObj<any> {
|
|
|
|
const visitor = {
|
|
|
|
CallExpression(path: NodePath<BabelTypes.CallExpression>, state: any) {
|
|
|
|
const onlyBuiltIns = state.opts.onlyBuiltIns
|
|
|
|
|
|
|
|
// if specified, options.lib is a list of libraries that provide hook functions
|
|
|
|
const libs =
|
|
|
|
state.opts.lib &&
|
|
|
|
(state.opts.lib === true
|
|
|
|
? ['react', 'preact/hooks']
|
|
|
|
: [].concat(state.opts.lib))
|
|
|
|
|
|
|
|
// skip function calls that are not the init of a variable declaration:
|
|
|
|
if (!t.isVariableDeclarator(path.parent)) return
|
|
|
|
|
|
|
|
// skip function calls where the return value is not Array-destructured:
|
|
|
|
if (!t.isArrayPattern(path.parent.id)) return
|
|
|
|
|
|
|
|
// name of the (hook) function being called:
|
|
|
|
const hookName = (path.node.callee as BabelTypes.Identifier).name
|
|
|
|
|
|
|
|
if (libs) {
|
|
|
|
const binding = path.scope.getBinding(hookName)
|
|
|
|
// not an import
|
|
|
|
if (!binding || binding.kind !== 'module') return
|
|
|
|
|
|
|
|
const specifier = (binding.path.parent as BabelTypes.ImportDeclaration)
|
|
|
|
.source.value
|
|
|
|
// not a match
|
2021-06-18 00:43:25 +02:00
|
|
|
if (!libs.some((lib: any) => lib === specifier)) return
|
2019-08-16 00:08:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// only match function calls with names that look like a hook
|
|
|
|
if (!(onlyBuiltIns ? isBuiltInHook : isHook).test(hookName)) return
|
|
|
|
|
|
|
|
path.parent.id = t.objectPattern(
|
2019-08-22 17:21:34 +02:00
|
|
|
path.parent.id.elements.reduce<Array<BabelTypes.ObjectProperty>>(
|
|
|
|
(patterns, element, i) => {
|
|
|
|
if (element === null) {
|
|
|
|
return patterns
|
|
|
|
}
|
|
|
|
|
|
|
|
return patterns.concat(
|
|
|
|
t.objectProperty(t.numericLiteral(i), element)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
[]
|
2019-08-16 00:08:06 +02:00
|
|
|
)
|
|
|
|
)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
name: 'optimize-hook-destructuring',
|
|
|
|
visitor: {
|
|
|
|
// this is a workaround to run before preset-env destroys destructured assignments
|
|
|
|
Program(path, state) {
|
|
|
|
path.traverse(visitor, state)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|