项目的配置文件 .babel.config._ 或者 .babelrc._
module.exports=(api)=>{
api.cache(true);
return{
presets :[
[
"@babel/env",
{
"useBuiltIns": 'entry',
"corejs": "3.33.3"
}
]
],
plugins:[]
}
}
根据命令 npx babel src/index.js --out-file index.compiled.js 经过**@babel/cli**定位到 @babel/core 下的 transform-file.ts 文件
preset和plugin的大体加载流程
import loadConfig from "./config/index.ts";
const transformFileRunner = gensync(function* (
filename: string,
opts?: InputOptions,
): Handler<FileResult | null> {
const options = { ...opts, filename };
// 1. 加载项目的配置文件,解析presets,加载所有的plugins
/**
* {
* externalDependencies:[],
* options:{},
* passes:[[..plugins]]
* }
*/
const config: ResolvedConfig | null = yield* loadConfig(options);
if (config === null) return null;
const code = yield* fs.readFile(filename, "utf8");
return yield* run(config, code);
});
loadConfig 方法:加载项目的插件和预设,其中 options 对象:
const options={
filename:'src/index.js'
sourceFileName:'src/index.js'
}
loadConfig 方法来自 full.ts 的 loadFullConfig 方法
import loadPrivatePartialConfig from "./partial.ts";
export default gensync(function* loadFullConfig(
inputOpts: unknown,
): Handler<ResolvedConfig | null> {
// 1. result 存在 options ={plugins,presets},数组中每个plugin和preset存在value属性值 为其库的导出函数
const result = yield* loadPrivatePartialConfig(inputOpts);
const { options, context, fileHandling } = result;
...
const { plugins, presets } = options;
...
// 没有进行加载的 presets的Descriptors
const presetsDescriptors = presets.map(toDescriptor);
const initialPluginsDescriptors = plugins.map(toDescriptor);
const pluginDescriptorsByPass: Array<Array<UnloadedDescriptor<PluginAPI>>> = [
[],
];
const passes: Array<Array<Plugin>> = [];
...
const externalDependencies: DeepArray<string> = [];
// 递归其中的所有preset 包括其中嵌套的presets,统计presets中的所有plugins
const ignored = yield* enhanceError(
context,
// 递归加载 preset,统计出所有的 plugins
function* recursePresetDescriptors(
rawPresets: Array<UnloadedDescriptor<PresetAPI>>,
pluginDescriptorsPass: Array<UnloadedDescriptor<PluginAPI>>,
): Handler<true | void> {
const presets: Array<{
preset: ConfigChain | null;
pass: Array<UnloadedDescriptor<PluginAPI>>;
}> = [];
for (let i = 0; i < rawPresets.length; i++) {
const descriptor = rawPresets[i];
if (descriptor.options !== false) {
try {
// eslint-disable-next-line no-var
// 加载解析预设,返回配置对象{chain:{plugins:[..],presets:[...]}}
var preset = yield* loadPresetDescriptor(descriptor, presetContext);
} catch (e) {
if (e.code === "BABEL_UNKNOWN_OPTION") {
checkNoUnwrappedItemOptionPairs(rawPresets, i, "preset", e);
}
throw e;
}
// 外部依赖项
externalDependencies.push(preset.externalDependencies);
// 预设 通常按照相反的顺序执行,从后往前执行。但是指定自己的通行,他们会运行在预设之后
if (descriptor.ownPass) {
presets.push({ preset: preset.chain, pass: [] });
} else {
// 预设数组,每个预设 都包括preset属性,其为对象包括该预设的解析后的 plugins 和 presets
/**
* presets=[
* {
* preset:{
* plugins:[...],
* presets:[...]
* }
* pass:[]
* }
* ]
*/
presets.unshift({
preset: preset.chain,
pass: pluginDescriptorsPass,
});
}
}
}
// resolve presets
if (presets.length > 0) {
// The passes are created in the same order as the preset list, but are inserted before any
// existing additional passes.
// 过程的创建顺序与预设列表相同,但插入在任何现有的附加过程之前
// 暂时没有发现 有什么作用
pluginDescriptorsByPass.splice(
1,
0,
...presets.map(o => o.pass).filter(p => p !== pluginDescriptorsPass),
);
for (const { preset, pass } of presets) {
if (!preset) return true;
// 记录本预设的 所有plugin,放在 pluginDescriptorsByPass[0]中
pass.push(...preset.plugins);
// 传入 递归recursePresetDescriptors方法中
const ignored = yield* recursePresetDescriptors(preset.presets, pass);
if (ignored) return true;
preset.options.forEach(opts => {
mergeOptions(optionDefaults, opts);
});
}
}
},
)(presetsDescriptors, pluginDescriptorsByPass[0]);
...
yield* enhanceError(context, function* loadPluginDescriptors() {
pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors);
for (const descs of pluginDescriptorsByPass) {
const pass: Plugin[] = [];
passes.push(pass);
for (let i = 0; i < descs.length; i++) {
const descriptor = descs[i];
if (descriptor.options !== false) {
try {
// eslint-disable-next-line no-var
var plugin = yield* loadPluginDescriptor(descriptor, pluginContext);
} catch (e) {
if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") {
// print special message for `plugins: ["@babel/foo", { foo: "option" }]`
checkNoUnwrappedItemOptionPairs(descs, i, "plugin", e);
}
throw e;
}
pass.push(plugin);
externalDependencies.push(plugin.externalDependencies);
}
}
}
})();
return {
options: opts,
passes: passes,
externalDependencies: freezeDeepArray(externalDependencies),
};
});
/*******************************preset加载执行*******************************/
// 生成一个配置对象,该对象将作为新嵌套配置的根
// 通过预设descriptor,加载其中的插件和预设,生成配置对象{chain,externalDependencies},其中chain为查找的关键字,通过buildPresetChain方法 还会记载其中的 presets和plugins。
// 通过 recursePresetDescriptors 方法 一直调用并生成配置对象
function* loadPresetDescriptor(
descriptor: UnloadedDescriptor<PresetAPI>,
context: Context.FullPreset,
): Handler<{
chain: ConfigChain | null;
externalDependencies: ReadonlyDeepArray<string>;
}> {
const preset = instantiatePreset(
// 执行 descriptor 的value函数(即 预设库的加载)
// 以 @babel/env 为例,即执行export default方法 返回其中的plugins等参数
yield* presetDescriptorLoader(descriptor, context),
);
validatePreset(preset, context, descriptor);
return {
// 进一步 加载预设解析后 存在的plugins 和 presets
chain: yield* buildPresetChain(preset, context),
externalDependencies: preset.externalDependencies,
};
}
/*******************************plugin加载执行*******************************/
// 返回一个plugin对象{visitor,pre,post}
function* loadPluginDescriptor(descriptor, context) {
if (descriptor.value instanceof _plugin.default) {
if (descriptor.options) {
throw new Error("Passed options to an existing Plugin instance will not work.");
}
return descriptor.value;
}
return yield* instantiatePlugin(yield* pluginDescriptorLoader(descriptor, context), context);
}
loadPrivatePartialConfig 来自 partial.ts 的导出函数
import { buildRootChain } from "./config-chain.ts";
export default function* loadPrivatePartialConfig(
inputOpts: unknown,
): Handler<PrivPartialConfig | null> {
...
// 同 loadConfig方法的参数 options 一致
const args = inputOpts ? validate("arguments", inputOpts) : {};
...
// 经过上面省略的步骤 获取项目的基本路径
const context: ConfigContext = {
filename, // 需要执行转换的文件路径,即:src/index.js 的绝对路径
cwd: absoluteCwd, // 脚本文件所在的路径,即:项目的根路径 本项目babelSimpleDemo
root: absoluteRootDir,// 项目根路径,即:babelSimpleDemo
envName,
caller,
showConfig: showConfigPath === filename,// false
};
// 根据 文件路径信息 加载配置链
const configChain = yield* buildRootChain(args, context);
...
const options: NormalizedOptions = {
...merged,
targets: resolveTargets(merged, absoluteRootDir),
cloneInputAst,
babelrc: false,
configFile: false,
browserslistConfigFile: false,
passPerPreset: false,
envName: context.envName,
cwd: context.cwd,
root: context.root,
rootMode: "root",
filename:
typeof context.filename === "string" ? context.filename : undefined,
plugins: configChain.plugins.map(descriptor =>
// 构建 configItem 有value属性
createItemFromDescriptor(descriptor),
),
presets: configChain.presets.map(descriptor =>
// 构建 configItem 有value属性
createItemFromDescriptor(descriptor),
),
};
return {
options,
context,
fileHandling: configChain.fileHandling,
ignore: configChain.ignore,
babelrc: configChain.babelrc,
config: configChain.config,
files: configChain.files,
};
}
buildRootChain 来自 config-chain.ts 文件
加载 plugins 和 presets,有两种方式查找,可以同时存在
npx babel --plugins @babel/plugin-transform-arrow-functions src/index.js --out-file index.compiled.js
见文件顶部配置代码
export function* buildRootChain(
opts: ValidatedOptions,
context: ConfigContext,
): Handler<RootConfigChain | null> {
let configReport, babelRcReport;
const programmaticLogger = new ConfigPrinter();
// 通过代码调用或者命令行直接指定的plugins和presets配置
// 返回 plugin 和 preset 的库的导出函数export.modules 以及 browsersList
// 命令行 npx babel --plugins @babel/plugin-transform-arrow-functions src/index.js --out-file index.compiled.js
const programmaticChain = yield* loadProgrammaticChain(
{
options: opts,
dirname: context.cwd,
},
context,
undefined,
programmaticLogger,
);
if (!programmaticChain) return null;
const programmaticReport = yield* programmaticLogger.output();
let configFile;
// 命令行或者代码程序中 是否指定了 configFile,本案例中没有指定
if (typeof opts.configFile === "string") {
configFile = yield* loadConfig(
opts.configFile,
context.cwd,
context.envName,
context.caller,
);
} else if (opts.configFile !== false) {
// 加载根目录配置,即加载 babel.config.[js|cjs|mjs|json|cts]
configFile = yield* findRootConfig(
context.root,
context.envName,
context.caller,
);
}
// 该测试项目 使用的是 .babelrc.js,所以 上面的 configFile 不存在,省略下面代码
...
let ignoreFile, babelrcFile;
let isIgnored = false;
const fileChain = emptyChain();
// resolve all .babelrc files
if (
(babelrc === true || babelrc === undefined) &&
typeof context.filename === "string"
) {
// 根据filename 逐级向上查找路径并返回
// 主要是找到包的元数据(路径,package.js信息),以便来查找问价下面的所有的 .babelrc.* 配置文件
const pkgData = yield* findPackageData(context.filename);
if (
pkgData &&
babelrcLoadEnabled(context, pkgData, babelrcRoots, babelrcRootsDirectory)
) {
// 内部 根据pkgData.directories数组 循环查找配置 babelrc.*
//(见configuration.ts中findRelativeConfig 和 loadOneConfig)
/**
* babelrcFile 为:
* {
* filepath,
* dirname,
* options:{
* plugins[],
* presets:[['@babel/env',{core-js:3.xx,useBuiltIns:'xx'}]]
* }
* }
*
*/
({ ignore: ignoreFile, config: babelrcFile } = yield* findRelativeConfig(
pkgData,
context.envName,
context.caller,
));
...
if (babelrcFile && !isIgnored) {
// 有效的配置文件 和 babelrcFile 一样
const validatedFile = validateBabelrcFile(babelrcFile);
const babelrcLogger = new ConfigPrinter();
// result 为: {files,options,plugins,presets},
// 其中包含plugin 和 preset 库的 导出函数 以及 browsersList
const result = yield* loadFileChain(
validatedFile,
context,
undefined,
babelrcLogger,
);
if (!result) {
isIgnored = true;
} else {
babelRcReport = yield* babelrcLogger.output();
mergeChain(fileChain, result);
}
}
if (babelrcFile && isIgnored) {
fileChain.files.add(babelrcFile.filepath);
}
}
}
// Insert file chain in front so programmatic options have priority
// over configuration file chain items.
const chain = mergeChain(
mergeChain(mergeChain(emptyChain(), configFileChain), fileChain),
programmaticChain,
);
// dedupDescriptors 去重
// 返回包括:各种plugin 和 preset的库的 导出函数 (通过 require 加载模块得到的函数)
return {
plugins: isIgnored ? [] : dedupDescriptors(chain.plugins),
presets: isIgnored ? [] : dedupDescriptors(chain.presets),
options: isIgnored ? [] : chain.options.map(o => normalizeOptions(o)),
fileHandling: isIgnored ? "ignored" : "transpile",
ignore: ignoreFile || undefined,
babelrc: babelrcFile || undefined,
config: configFile || undefined,
files: chain.files,
};
}
/*********************************** 1. 来自 full.ts的调用,主要是加载
解析后的预设中存在的presets 和 plugins***************************** */
export function* buildPresetChain(
arg: PresetInstance,
context: any,
): Handler<ConfigChain | null> {
const chain = yield* buildPresetChainWalker(arg, context);
if (!chain) return null;
return {
plugins: dedupDescriptors(chain.plugins),
presets: dedupDescriptors(chain.presets),
options: chain.options.map(o => normalizeOptions(o)),
files: new Set(),
};
}
export const buildPresetChainWalker = makeChainWalker<PresetInstance>({
root: preset => loadPresetDescriptors(preset),
env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
overridesEnv: (preset, index, envName) =>
loadPresetOverridesEnvDescriptors(preset)(index)(envName),
createLogger: () => () => {}, // Currently we don't support logging how preset is expanded
});
const loadPresetDescriptors = makeWeakCacheSync((preset: PresetInstance) =>
buildRootDescriptors(preset, preset.alias, createUncachedDescriptors),
);
/***********************************代码或者命令行传入的plugins 和presets***************************** */
// 内部调用 root方法 -- buildRootDescriptors-- 最终执行的是 传入的 createCachedDescriptors
const loadProgrammaticChain = makeChainWalker({
root: input => buildRootDescriptors(input, "base", createCachedDescriptors),
...
});
// 同 loadConfig方法的参数 options 一致,其中plugins和presets来自 代码传入或者命令行
function buildRootDescriptors(
{ dirname, options }: Partial<ValidatedFile>,
alias: string,
descriptors: (
dirname: string,
options: ValidatedOptions,
alias: string,
) => OptionsAndDescriptors,
) {
return descriptors(dirname, options, alias);
}
/***********************************配置文件查找的的plugins 和presets***************************** */
function* loadFileChain(
input: ValidatedFile,
context: ConfigContext,
files: Set<ConfigFile>,
baseLogger: ConfigPrinter,
) {
const chain = yield* loadFileChainWalker(input, context, files, baseLogger);
chain?.files.add(input.filepath);
return chain;
}
const loadFileChainWalker = makeChainWalker<ValidatedFile>({
root: file => loadFileDescriptors(file),
env: (file, envName) => loadFileEnvDescriptors(file)(envName),
overrides: (file, index) => loadFileOverridesDescriptors(file)(index),
overridesEnv: (file, index, envName) =>
loadFileOverridesEnvDescriptors(file)(index)(envName),
createLogger: (file, context, baseLogger) =>
buildFileLogger(file.filepath, context, baseLogger),
});
const loadFileDescriptors = makeWeakCacheSync((file: ValidatedFile) =>
// 其中 file 就是 configFile/ValidatedFile
buildRootDescriptors(file, file.filepath, createUncachedDescriptors),
);
其中 代码和命令行执行的加载 plugins 和 presets 以及 配置文件执行加载 plugins 和 presets
最终执行都为 buildRootDescriptors(注:createCachedDescriptors和createUncachedDescriptors)
import {
createCachedDescriptors,
createUncachedDescriptors,
} from "./config-descriptors.ts";
其中都 引用了 files下plugins.ts 的 loadPreset 和loadPreset方法
export {
loadPlugin,
loadPreset,
resolvePlugin,
resolvePreset,
} from "./plugins.ts";
config-chain.ts 文件中的 loadConfig、findRootConfig、findPackageData、findRelativeConfig 方法来自 files 文件夹
注:files.ts相关配置文件加载
插件加载顺序:
{
"plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
先执行 transform-decorators-legacy ,在执行 transform-class-properties
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
将按如下顺序执行: 首先是 @babel/preset-react,然后是 @babel/preset-env。