rsnext/packages/next/build/babel/loader/transform.ts
Dale Bustad c2dfe40296
Babel fast mode (#23760)
@timneutkens it'd be great to get your input.

These changes introduce a new Babel loader that eliminates much of the existing overhead, resulting in better HMR speeds. 
 Multithreading is still in flight, and may be omitted if speed improvements end up being negligible.  For now, the new loader is hidden behind an `experimental` flag.

Items to be completed before this PR is ready to merge:

- [x] reconfigure `ncc` to precompile the parts of `@babel/core` and `@babel/traverse` that we're accessing directly
- [x] change `@babel/core/...` imports to `ncc`ed version
- [x] ~~measure multithreading (not currently pushed) functionality, and include the functionality depending on the results~~ I'll open a separate PR for this
- [x] ensure TypeScript is happy with all imports as final step (`--no-verify` was used to bypass)

There will be two follow-up PRs:
- loader support for projects with custom `.babelrc`
- multithreaded loader (should the change we warranted after measurement)
2021-04-08 12:03:02 +00:00

106 lines
3 KiB
TypeScript

/*
* Partially adapted from @babel/core (MIT license).
*/
import traverse from 'next/dist/compiled/babel/traverse'
import generate from 'next/dist/compiled/babel/generator'
import normalizeFile from 'next/dist/compiled/babel/core-lib-normalize-file'
import normalizeOpts from 'next/dist/compiled/babel/core-lib-normalize-opts'
import loadBlockHoistPlugin from 'next/dist/compiled/babel/core-lib-block-hoist-plugin'
import PluginPass from 'next/dist/compiled/babel/core-lib-plugin-pass'
import getConfig from './get-config'
import { consumeIterator } from './util'
import { Span } from '../../../telemetry/trace'
import { NextJsLoaderContext } from './types'
function getTraversalParams(file: any, pluginPairs: any[]) {
const passPairs = []
const passes = []
const visitors = []
for (const plugin of pluginPairs.concat(loadBlockHoistPlugin())) {
const pass = new PluginPass(file, plugin.key, plugin.options)
passPairs.push([plugin, pass])
passes.push(pass)
visitors.push(plugin.visitor)
}
return { passPairs, passes, visitors }
}
function invokePluginPre(file: any, passPairs: any[]) {
for (const [{ pre }, pass] of passPairs) {
if (pre) {
pre.call(pass, file)
}
}
}
function invokePluginPost(file: any, passPairs: any[]) {
for (const [{ post }, pass] of passPairs) {
if (post) {
post.call(pass, file)
}
}
}
function transformAstPass(file: any, pluginPairs: any[], parentSpan: Span) {
const { passPairs, passes, visitors } = getTraversalParams(file, pluginPairs)
invokePluginPre(file, passPairs)
const visitor = traverse.visitors.merge(
visitors,
passes,
// @ts-ignore - the exported types are incorrect here
file.opts.wrapPluginVisitorMethod
)
parentSpan
.traceChild('babel-turbo-traverse')
.traceFn(() => traverse(file.ast, visitor, file.scope))
invokePluginPost(file, passPairs)
}
function transformAst(file: any, babelConfig: any, parentSpan: Span) {
for (const pluginPairs of babelConfig.passes) {
transformAstPass(file, pluginPairs, parentSpan)
}
}
export default function transform(
this: NextJsLoaderContext,
source: string,
inputSourceMap: object | null | undefined,
loaderOptions: any,
filename: string,
target: string,
parentSpan: Span
) {
const getConfigSpan = parentSpan.traceChild('babel-turbo-get-config')
const babelConfig = getConfig.call(this, {
source,
loaderOptions,
inputSourceMap,
target,
filename,
})
getConfigSpan.stop()
const normalizeSpan = parentSpan.traceChild('babel-turbo-normalize-file')
const file = consumeIterator(
normalizeFile(babelConfig.passes, normalizeOpts(babelConfig), source)
)
normalizeSpan.stop()
const transformSpan = parentSpan.traceChild('babel-turbo-transform')
transformAst(file, babelConfig, transformSpan)
transformSpan.stop()
const generateSpan = parentSpan.traceChild('babel-turbo-generate')
const { code, map } = generate(file.ast, file.opts.generatorOpts, file.code)
generateSpan.stop()
return { code, map }
}