265 lines
11 KiB
JavaScript

/**
* Entry point for @babel/standalone. This wraps Babel's API in a version that's
* friendlier for use in web browers. It removes the automagical detection of
* plugins, instead explicitly registering all the available plugins and
* presets, and requiring custom ones to be registered through `registerPlugin`
* and `registerPreset` respectively.
*/
/* global VERSION */
/* eslint-disable max-len */
import * as Babel from "@babel/core";
import { runScripts } from "./transformScriptTags";
const isArray =
Array.isArray ||
(arg => Object.prototype.toString.call(arg) === "[object Array]");
/**
* Loads the given name (or [name, options] pair) from the given table object
* holding the available presets or plugins.
*
* Returns undefined if the preset or plugin is not available; passes through
* name unmodified if it (or the first element of the pair) is not a string.
*/
function loadBuiltin(builtinTable, name) {
if (isArray(name) && typeof name[0] === "string") {
if (builtinTable.hasOwnProperty(name[0])) {
return [builtinTable[name[0]]].concat(name.slice(1));
}
return;
} else if (typeof name === "string") {
return builtinTable[name];
}
// Could be an actual preset/plugin module
return name;
}
/**
* Parses plugin names and presets from the specified options.
*/
function processOptions(options) {
// Parse preset names
const presets = (options.presets || []).map(presetName => {
const preset = loadBuiltin(availablePresets, presetName);
if (preset) {
// workaround for babel issue
// at some point, babel copies the preset, losing the non-enumerable
// buildPreset key; convert it into an enumerable key.
if (
isArray(preset) &&
typeof preset[0] === "object" &&
preset[0].hasOwnProperty("buildPreset")
) {
preset[0] = { ...preset[0], buildPreset: preset[0].buildPreset };
}
} else {
throw new Error(
`Invalid preset specified in Babel options: "${presetName}"`,
);
}
return preset;
});
// Parse plugin names
const plugins = (options.plugins || []).map(pluginName => {
const plugin = loadBuiltin(availablePlugins, pluginName);
if (!plugin) {
throw new Error(
`Invalid plugin specified in Babel options: "${pluginName}"`,
);
}
return plugin;
});
return {
babelrc: false,
...options,
presets,
plugins,
};
}
export function transform(code, options) {
return Babel.transform(code, processOptions(options));
}
export function transformFromAst(ast, code, options) {
return Babel.transformFromAst(ast, code, processOptions(options));
}
export const availablePlugins = {};
export const availablePresets = {};
export const buildExternalHelpers = Babel.buildExternalHelpers;
/**
* Registers a named plugin for use with Babel.
*/
export function registerPlugin(name, plugin) {
if (availablePlugins.hasOwnProperty(name)) {
console.warn(
`A plugin named "${name}" is already registered, it will be overridden`,
);
}
availablePlugins[name] = plugin;
}
/**
* Registers multiple plugins for use with Babel. `newPlugins` should be an object where the key
* is the name of the plugin, and the value is the plugin itself.
*/
export function registerPlugins(newPlugins) {
Object.keys(newPlugins).forEach(name =>
registerPlugin(name, newPlugins[name]),
);
}
/**
* Registers a named preset for use with Babel.
*/
export function registerPreset(name, preset) {
if (availablePresets.hasOwnProperty(name)) {
console.warn(
`A preset named "${name}" is already registered, it will be overridden`,
);
}
availablePresets[name] = preset;
}
/**
* Registers multiple presets for use with Babel. `newPresets` should be an object where the key
* is the name of the preset, and the value is the preset itself.
*/
export function registerPresets(newPresets) {
Object.keys(newPresets).forEach(name =>
registerPreset(name, newPresets[name]),
);
}
// All the plugins we should bundle
// Want to get rid of this long whitelist of plugins?
// Wait! Please read https://github.com/babel/babel/pull/6177 first.
registerPlugins({
"check-constants": require("@babel/plugin-check-constants"),
"external-helpers": require("@babel/plugin-external-helpers"),
"syntax-async-generators": require("@babel/plugin-syntax-async-generators"),
"syntax-class-properties": require("@babel/plugin-syntax-class-properties"),
"syntax-decorators": require("@babel/plugin-syntax-decorators"),
"syntax-do-expressions": require("@babel/plugin-syntax-do-expressions"),
"syntax-dynamic-import": require("@babel/plugin-syntax-dynamic-import"),
"syntax-export-default-from": require("@babel/plugin-syntax-export-default-from"),
"syntax-export-namespace-from": require("@babel/plugin-syntax-export-namespace-from"),
"syntax-flow": require("@babel/plugin-syntax-flow"),
"syntax-function-bind": require("@babel/plugin-syntax-function-bind"),
"syntax-function-sent": require("@babel/plugin-syntax-function-sent"),
"syntax-import-meta": require("@babel/plugin-syntax-import-meta"),
"syntax-jsx": require("@babel/plugin-syntax-jsx"),
"syntax-object-rest-spread": require("@babel/plugin-syntax-object-rest-spread"),
"syntax-optional-catch-binding": require("@babel/plugin-syntax-optional-catch-binding"),
"transform-async-to-generator": require("@babel/plugin-transform-async-to-generator"),
"proposal-class-properties": require("@babel/plugin-proposal-class-properties"),
"proposal-decorators": require("@babel/plugin-proposal-decorators"),
"proposal-do-expressions": require("@babel/plugin-proposal-do-expressions"),
"proposal-export-default-from": require("@babel/plugin-proposal-export-default-from"),
"proposal-export-namespace-from": require("@babel/plugin-proposal-export-namespace-from"),
"transform-arrow-functions": require("@babel/plugin-transform-arrow-functions"),
"transform-block-scoped-functions": require("@babel/plugin-transform-block-scoped-functions"),
"transform-block-scoping": require("@babel/plugin-transform-block-scoping"),
"transform-classes": require("@babel/plugin-transform-classes"),
"transform-computed-properties": require("@babel/plugin-transform-computed-properties"),
"transform-destructuring": require("@babel/plugin-transform-destructuring"),
"transform-duplicate-keys": require("@babel/plugin-transform-duplicate-keys"),
"transform-for-of": require("@babel/plugin-transform-for-of"),
"transform-function-name": require("@babel/plugin-transform-function-name"),
"transform-instanceof": require("@babel/plugin-transform-instanceof"),
"transform-literals": require("@babel/plugin-transform-literals"),
"transform-modules-amd": require("@babel/plugin-transform-modules-amd"),
"transform-modules-commonjs": require("@babel/plugin-transform-modules-commonjs"),
"transform-modules-systemjs": require("@babel/plugin-transform-modules-systemjs"),
"transform-modules-umd": require("@babel/plugin-transform-modules-umd"),
"transform-object-super": require("@babel/plugin-transform-object-super"),
"transform-parameters": require("@babel/plugin-transform-parameters"),
"transform-shorthand-properties": require("@babel/plugin-transform-shorthand-properties"),
"transform-spread": require("@babel/plugin-transform-spread"),
"transform-sticky-regex": require("@babel/plugin-transform-sticky-regex"),
"transform-template-literals": require("@babel/plugin-transform-template-literals"),
"transform-typeof-symbol": require("@babel/plugin-transform-typeof-symbol"),
"transform-unicode-regex": require("@babel/plugin-transform-unicode-regex"),
"transform-member-expression-literals": require("@babel/plugin-transform-member-expression-literals"),
"transform-property-literals": require("@babel/plugin-transform-property-literals"),
"transform-property-mutators": require("@babel/plugin-transform-property-mutators"),
"transform-eval": require("@babel/plugin-transform-eval"),
"transform-exponentiation-operator": require("@babel/plugin-transform-exponentiation-operator"),
"transform-flow-comments": require("@babel/plugin-transform-flow-comments"),
"transform-flow-strip-types": require("@babel/plugin-transform-flow-strip-types"),
"proposal-function-bind": require("@babel/plugin-proposal-function-bind"),
"transform-jscript": require("@babel/plugin-transform-jscript"),
"transform-new-target": require("@babel/plugin-transform-new-target"),
"transform-object-assign": require("@babel/plugin-transform-object-assign"),
"proposal-object-rest-spread": require("@babel/plugin-proposal-object-rest-spread"),
"transform-object-set-prototype-of-to-assign": require("@babel/plugin-transform-object-set-prototype-of-to-assign"),
"proposal-optional-catch-binding": require("@babel/plugin-proposal-optional-catch-binding"),
"transform-proto-to-assign": require("@babel/plugin-transform-proto-to-assign"),
"transform-react-constant-elements": require("@babel/plugin-transform-react-constant-elements"),
"transform-react-display-name": require("@babel/plugin-transform-react-display-name"),
"transform-react-inline-elements": require("@babel/plugin-transform-react-inline-elements"),
"transform-react-jsx": require("@babel/plugin-transform-react-jsx"),
"transform-react-jsx-compat": require("@babel/plugin-transform-react-jsx-compat"),
"transform-react-jsx-self": require("@babel/plugin-transform-react-jsx-self"),
"transform-react-jsx-source": require("@babel/plugin-transform-react-jsx-source"),
"transform-regenerator": require("@babel/plugin-transform-regenerator"),
"transform-runtime": require("@babel/plugin-transform-runtime"),
"transform-strict-mode": require("@babel/plugin-transform-strict-mode"),
"proposal-unicode-property-regex": require("@babel/plugin-proposal-unicode-property-regex"),
});
// All the presets we should bundle
// Want to get rid of this whitelist of presets?
// Wait! Please read https://github.com/babel/babel/pull/6177 first.
registerPresets({
es2015: require("@babel/preset-es2015"),
es2016: require("@babel/preset-es2016"),
es2017: require("@babel/preset-es2017"),
react: require("@babel/preset-react"),
"stage-0": require("@babel/preset-stage-0"),
"stage-1": require("@babel/preset-stage-1"),
"stage-2": require("@babel/preset-stage-2"),
"stage-3": require("@babel/preset-stage-3"),
"es2015-loose": {
presets: [[require("@babel/preset-es2015"), { loose: true }]],
},
// ES2015 preset with es2015-modules-commonjs removed
"es2015-no-commonjs": {
presets: [[require("@babel/preset-es2015"), { modules: false }]],
},
typescript: require("@babel/preset-typescript"),
flow: require("@babel/preset-flow"),
});
export const version = VERSION;
// Listen for load event if we're in a browser and then kick off finding and
// running of scripts with "text/babel" type.
if (typeof window !== "undefined" && window && window.addEventListener) {
window.addEventListener(
"DOMContentLoaded",
() => transformScriptTags(),
false,
);
}
/**
* Transform <script> tags with "text/babel" type.
* @param {Array} scriptTags specify script tags to transform, transform all in the <head> if not given
*/
export function transformScriptTags(scriptTags) {
runScripts(transform, scriptTags);
}
/**
* Disables automatic transformation of <script> tags with "text/babel" type.
*/
export function disableScriptTags() {
window.removeEventListener("DOMContentLoaded", transformScriptTags);
}