64850a8348
This adds inlining for Babel and the Babel plugins used in next. This is based to the PR at https://github.com/vercel/next.js/pull/18823. The approach is to make one large bundle and then separate out the individual packages from that in order to avoid duplications. In the first attempt the Babel bundle size was 10MB... using "resolutions" in the Yarn workspace to reduce the duplicated packages this was brought down to a 2.8MB bundle for Babel and all the used plugins which is exactly the expected file size here. This will thus add a 2.8MB download size to the next package, but save downloading any babel dependencies separately, removing a large number of package dependencies from the overall install.
78 lines
2.3 KiB
TypeScript
78 lines
2.3 KiB
TypeScript
import {
|
|
NodePath,
|
|
PluginObj,
|
|
types as BabelTypes,
|
|
} from 'next/dist/compiled/babel/core'
|
|
|
|
// 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)$/
|
|
|
|
export default function ({
|
|
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
|
|
if (!libs.some((lib) => lib === specifier)) return
|
|
}
|
|
|
|
// only match function calls with names that look like a hook
|
|
if (!(onlyBuiltIns ? isBuiltInHook : isHook).test(hookName)) return
|
|
|
|
path.parent.id = t.objectPattern(
|
|
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)
|
|
)
|
|
},
|
|
[]
|
|
)
|
|
)
|
|
},
|
|
}
|
|
|
|
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)
|
|
},
|
|
},
|
|
}
|
|
}
|