/
githubmirror
/
babel
Обзор
Документация
Войти
/
githubmirror
/
babel
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/babel-node/src/_babel-node.ts
250 строк
6 KB
Nicolò Ribaudo
Fallback to assuming ESM support with `modules: auto` (#18069)
16 июн 2026, 11:03
Не верифицирован
16 июн 2026, 11:03
004f4b4
Код
Авторство
О чём код?
import Module from "node:module"; import { inspect } from "node:util"; import path from "node:path"; import repl from "node:repl"; import * as babel from "@babel/core"; import vm from "node:vm"; import "core-js/stable/index.js"; import register from "@babel/register"; import { fileURLToPath } from "node:url"; import { createRequire } from "node:module"; import type { PluginAPI, PluginObject } from "@babel/core"; import { program } from "./program-setup.ts"; const require = createRequire(import.meta.url); program.parse(process.argv); const opts = program.opts(); const babelOptions = { caller: { name: "@babel/node", supportsStaticESM: false, supportsDynamicImport: false, supportsExportNamespaceFrom: false, }, extensions: opts.extensions, ignore: opts.ignore, only: opts.only, plugins: opts.plugins, presets: opts.presets, configFile: opts.configFile, envName: opts.envName, rootMode: opts.rootMode, // Commander will default the "--no-" arguments to true, but we want to // leave them undefined so that @babel/core can handle the // default-assignment logic on its own. babelrc: opts.babelrc === true ? undefined : opts.babelrc, }; for (const key of Object.keys(babelOptions) as (keyof typeof babelOptions)[]) { if (babelOptions[key] === undefined) { delete babelOptions[key]; } } register(babelOptions); let hasTopLevelAwait = false; const replPlugin = ({ types: t }: PluginAPI): PluginObject => ({ visitor: { Program(path) { hasTopLevelAwait = path.node.extra?.topLevelAwait as boolean; let hasExpressionStatement: boolean | undefined; for (const bodyPath of path.get("body")) { if (bodyPath.isExpressionStatement()) { hasExpressionStatement = true; } else if ( bodyPath.isExportDeclaration() || bodyPath.isImportDeclaration() ) { throw bodyPath.buildCodeFrameError( "Modules aren't supported in the REPL", ); } } if (hasTopLevelAwait) { const body = path.node.body; for (let i = body.length - 1; i >= 0; i--) { if (t.isExpressionStatement(body[i])) { body[i] = t.returnStatement( (body[i] as babel.types.ExpressionStatement).expression, ); break; } } return; } if (hasExpressionStatement) return; // If the executed code doesn't evaluate to a value, // prevent implicit strict mode from printing 'use strict'. path.pushContainer( "body", t.expressionStatement(t.identifier("undefined")), ); }, }, }); const _eval = function (code: string, filename: string) { code = code.trim(); if (!code) return undefined; hasTopLevelAwait = false; code = babel.transformSync(code, { filename: filename, ...babelOptions, parserOpts: { allowAwaitOutsideFunction: true, }, plugins: (opts.plugins || []).concat([replPlugin]), })!.code!; if (hasTopLevelAwait) { code = `(async () => { ${code} })()`; } return vm.runInThisContext(code, { filename: filename, }); }; if (opts.eval || opts.print) { let code = opts.eval; if (!code || code === true) code = opts.print; global.__filename = "[eval]"; global.__dirname = process.cwd(); const module = new Module(global.__filename); module.filename = global.__filename; // @ts-expect-error todo(flow->ts) module.paths = Module._nodeModulePaths(global.__dirname); global.exports = module.exports; global.module = module; // @ts-expect-error missing require.extensions global.require = module.require.bind(module); const result = _eval(code, global.__filename); if (opts.print) { const output = typeof result === "string" ? result : inspect(result); process.stdout.write(output + "\n"); } } else { if (program.args.length) { // slice all arguments up to the first filename since they're babel args that we handle let args = process.argv.slice(2); let i = 0; let ignoreNext = false; args.some(function (arg, i2) { if (ignoreNext) { ignoreNext = false; return; } if (arg.startsWith("-")) { const parsedOption = program.options.find((option: any) => { return option.long === arg || option.short === arg; }); if (parsedOption === undefined) { return; } const optionName = parsedOption.attributeName(); const parsedArg = opts[optionName]; if (optionName === "require" || (parsedArg && parsedArg !== true)) { ignoreNext = true; } } else { i = i2; return true; } }); args = args.slice(i); requireArgs(); // make the filename absolute const filename = args[0]; if (!path.isAbsolute(filename)) { args[0] = path.join(process.cwd(), filename); } // add back on node and concat the sliced args process.argv = ["node", ...args]; process.execArgv.push(fileURLToPath(import.meta.url)); Module.runMain(); } else { requireArgs(); replStart(); } } // We have to handle require ourselves, as we want to require it in the context of babel-register function requireArgs() { if (opts.require) { require( require.resolve(opts.require, { paths: [process.cwd()], }), ); } } function replEval( this: repl.REPLServer, code: string, context: vm.Context, filename: string, callback: (err: Error | null, result: any) => void, ) { let err; let result; try { if (code.startsWith("(") && code.endsWith(")")) { code = code.slice(1, -1); // remove "(" and ")" } result = _eval(code, filename); } catch (e) { err = e; } if (hasTopLevelAwait && !err) { (result as Promise<any>) .then(v => { callback(null, v); }) .catch(e => { callback(e, null); }); } else { callback(err, result); } } function replStart() { const replServer = repl.start({ prompt: "babel > ", input: process.stdin, output: process.stdout, eval: replEval, useGlobal: true, preview: true, }); const NODE_REPL_HISTORY = process.env.NODE_REPL_HISTORY; // @ts-expect-error setupHistory may be undefined replServer.setupHistory(NODE_REPL_HISTORY, () => {}); }