Skip to main content

Upgrade to Babel 8 (Integration)

Refer plugin developers or integration users to this document when upgrading to Babel 8.

info

Check out the v8-migration guide for other user-level changes.

AST Changes

high

  • Dynamic import() is parsed as an ImportExpression (#15682, #16114).

    // Example input
    import("foo", options);

    // AST in Babel 7
    {
    type: "CallExpression",
    callee: { type: "Import" },
    arguments: [
    StringLiteral("foo"),
    Identifier("options")
    ]
    }

    // AST in Babel 8
    {
    type: "ImportExpression",
    source: StringLiteral("foo"),
    options: Identifier("options")
    }

    Migration: You are encouraged to test your Babel plugins with the new AST, starting from v7.23.0, specifying { parserOpts: { createImportExpressions: true } } in the Babel config. For end users utilizing Babel plugins that rely on the legacy import() AST, it is possible to set createImportExpressions to false. Note that the Babel 7 import() AST is now considered deprecated, it does not support new ES features such as Source Phrase Imports. We will remove the createImportExpressions parser option in Babel 9.

  • Use an identifier for TSTypeParameter.name (#12829).

    For a TS type parameter node, node.name is a string in Babel 7 while in Babel 8 it is an Identifier.

    input.ts
    // T is a TSTypeParameter
    function process<T>(input: T): T {}

    Migration: If you have a customized plugin accessing the name of a type parameter node, use node.name.name in Babel 8.

  • Rename parameters to params, typeAnnotation to returnType for TSCallSignatureDeclaration, TSConstructSignatureDeclaration, TSFunctionType, TSConstructorType and TSMethodSignature (#9231, #13709)

    input.ts
    interface Foo {
    // TSCallSignatureDeclaration
    <T>(): string;

    // TSMethodSignature
    foo<T>(): string;

    // TSConstructSignatureDeclaration
    new <T>(): string;
    }

    // TSFunctionType
    type Bar = <T>() => string;

    // TSConstructorType
    type Baz = new <T>() => string;

    Migration: If you have a customized plugin accessing properties of these TS nodes, make adjustments accordingly:

    • For node.parameters in Babel 7, use node.params in Babel 8
    • For node.typeAnnotation in Babel 7, use node.returnType in Babel 8

medium

  • Split typeParameter of TSMappedType (#16733).

    For a TSMappedType node, the typeParameter attribute is split into key and constraint attributes. This is to align the AST for TS nodes with @typescript-eslint.

    // Example input
    let map1: { [P in string]: number; };

    // AST in Babel 7
    {
    type: "TSMappedType",
    typeParameter: {
    type: "TypeParameter",
    name: Identifier("P"),
    constraint: TSStringKeyword()
    },
    typeAnnotation: TSNumberKeyword(),
    }

    // AST in Babel 8
    {
    type: "TSMappedType",
    key: Identifier("P"),
    constraint: TSStringKeyword()
    typeAnnotation: TSNumberKeyword(),
    }

    Migration: If you have a customized plugin accessing typeParameter of a TSMappedType node, use node.key and node.constraint in Babel 8.

low

  • Don't generate TSParenthesizedType unless createParenthesizedExpression is enabled(#9546, #12608)

    input.ts
    type T = ({});
    // createParenthesizedExpression: true
    TSParenthesizedType { typeAnnotation: TSTypeLiteral { members: [] } }
    // createParenthesizedExpression: false
    TSTypeLiteral { members: [] }

    Migration: If you need informations about parentheses, specify the createParenthesizedExpression parser option.

    babel.config.json
    { "parserOpts": { "createParenthesizedExpression": true } }

    When createParenthesizedExpression is false, you can also use node.extra.parenthesized to detect whether node is wrapped in parentheses.

API Changes

All packages

high

  • Disallow importing internal files (#14013, #14179).

    Migration: Use the exported API only. If you are relying on Babel internals, please open an issue and let us know.

@babel/core

medium

  • Disallow using babel.transform, babel.transformFile, babel.transformFromAst, babel.parse, babel.loadOptions, babel.loadPartialConfig and babel.createConfigItem synchronously (#11110, #12695, #15869).

    Migration: The API above require a callback argument. If you are not providing a callback, please use their sync versions: babel.transformSync, babel.transformFileSync, babel.transformFromAstSync, babel.parseSync, babel.loadOptionsSync, babel.loadPartialConfigSync and babel.createConfigItemSync.

@babel/generator

low

  • Remove CodeGenerator class (#16126)

    Migration: In Babel 8 the undocumented CodeGenerator class has been removed, please use the default exported generate function instead.

    - new CodeGenerator(ast).generate()
    + generate(ast)

@babel/types

medium

  • Reject invalid identifier names in t.identifier builder (#10917).

    babel-plugin.js
    // Empty string is an invalid identifier name
    t.identifier("");

    Migration: Call t.identifier with a valid name.

  • Remove Super from the Expression alias (#14750).

    A Super node represents super in super call super() and super property super.foo. super can not be a standalone expression. In other words, t.isExpression(t.super()) will return false in Babel 8.

    Migration: Search usage of t.isExpression, t.assertsExpression and Expression alias in the plugin visitor, and if necessary, update the usage when you are handling super call and super property. For example,

    my-babel-plugin.js
    // Add `.foo` to an expression
    - if (t.isExpression(path.node)) {
    + if (t.isExpression(path.node) || t.isSuper(path.node)) {
    path.replaceWith(
    t.memberExpression(
    path.node,
    t.identifier("foo")
    ))
    }

    You don't have to update the usage if super() and super.foo is not involved:

    my-babel-plugin.js
    // define an expression as a computed key of `foo`
    if (t.isExpression(path.node)) {
    path.replaceWith(
    t.memberExpression(
    t.identifier("foo"),
    // `super` can not be a computed key, so we don't update `isExpression`
    path.node,
    /* computed */ true
    ))
    }

low

  • Remove t.jSX* and t.tS* builder aliases (#6989, #15527)

    Migration: Use t.jsx* and t.ts* instead. For example, replace t.jSXIdentifier("foo") with t.jsxIdentifier("foo").

  • Remove selfClosing argument from t.jsxElement (#14464)

    - t.jsxElement(openingElement, closingElement, children, selfClosing?: boolean)
    + t.jsxElement(openingElement, closingElement, children)

    Migration: The selfClosing argument was already not used in the builder. You can safely remove it.

  • Remove optional argument from t.memberExpression (#13407)

    - t.memberExpression(object, property, computed, optional?: boolean)
    + t.memberExpression(object, property, computed)

    Migration: The optional argument was already not used in the builder. You can safely remove it.

  • Remove the Noop node type (#12361)

    Migration: The Noop node is not used. If you are depending on the Noop node, please open an issue and talk about your use case.

  • Initialize indexers, callProperties and internalSlots in the node ObjectTypeAnnotation as an empty array in t.objectTypeAnnotation (#14465)

    Migration: In Babel 7 the builder t.objectTypeAnnotation initializes them as null, this is inconsistent with how @babel/parser will parse the Flow object type annotations. In Babel 8 the new default value [] matches the parser behaviour. Adapt to the new default value if you are depending on this.

  • Reject negative and NaN/infinite numbers from t.numericLiteral (#15802)

    babel-plugin.js
    // NumericLiterals must be non-negative finite numbers.
    t.numericLiteral(-1);

    Migration: Babel 7 silently ignores such invalid usage. Use t.valueToNode(-1) instead.

@babel/parser

low

  • Align Babel parser error codes between Flow and TypeScript (#13294)

    Migration: The error.code for OptionalBindingPattern is renamed as PatternIsOptional.

  • Remove updateContext field from tokens[].type returned from option tokens: true (#13768)

    babel-integration.js
    import { parse } from "@babel/parser";

    const { tokens } = parse("a = 42", { tokens: true });
    tokens[0].type;
    // Babel 7
    // { label: "name", updateContext: null, ...other properties }
    // Babel 8
    // { label: "name", ... other properties }

    Migration: This change probably won't affect your integration. The tokens[].type is an object storing meta information of a token as implementation details.

  • Tokenize private name #priv as a single privateName token (#13256)

    This change will affect your integration only when you are using tokens: true and are depending on the extra tokens AST output.

    babel-integration.js
    import { parse } from "@babel/parser";

    const { tokens } = parse("class C { #priv }", { tokens: true });
    tokens.filter(t => t.start >= 10 && t.end <= 15) // get tokens for `#priv`
    // Babel 7
    // [
    // Token (#) { value: "#", start: 10, end: 11 },
    // Token (name) { value: "priv", start: 11, end: 15 }
    // ]
    // Babel 8
    // [
    // Token (privateName) { value: "priv", start: 10, end: 15 }
    // ]

    Migration: Adapt to the new privateName token. If you want to restore to the Babel 7 behaviour, manually process the privateName token into the # token and the name token (example).

  • Tokenize string template as templateNonTail and templateTail (#13919)

    This change will affect your integration only when you are using tokens: true and are depending on the extra tokens AST output.

    babel-integration.js
    import { parse } from "@babel/parser";

    const { tokens } = parse("`head${x}middle${y}tail`", { tokens: true });
    console.log(tokens); // print tokens
    // Babel 7
    // [
    // Token (`),
    // Token (template) { value: "head" }, Token (${),
    // Token (name) { value: "x" }, Token (}),
    // Token (template) { value: "middle" }, Token (${),
    // Token (name) { value: "y" }, Token (}),
    // Token (template) { value: "tail" }
    // Token (`)
    // ]
    // Babel 8
    // [
    // Token (templateNonTail) { value: "head" },
    // Token (name) { value: "x" },
    // Token (templateNonTail) { value: "middle" },
    // Token (name) { value: "y" },
    // Token (templateTail) { value: "tail" }
    // ]

    Migration: Adapt to the new token design. If you want to restore to the Babel 7 behaviour, manually transform them to the Babel 7 tokens (example).

  • Remove extra.shorthand from ObjectProperty nodes (#16521)

    Migration: Use node.shorthand rather than node.extra.shorthand.

@babel/traverse

medium

  • Remove some NodePath methods (#16655)

    Migration: hoist, updateSiblingKeys, call, setScope, resync, popContext, pushContext, setup, setKey These methods are meant to be private so there is no real migration approach. But if your plugin / build is broken by this change, feel free to open an issue and tell us how you use these methods and we can see what we can do after Babel 8 is released.

    is, isnt, has, equals Access path.node instead.

    - functionExpressionPath.equals("id", idNode)
    + functionExpressionPath.node.id === idNode

    - functionExpressionPath.is("id")
    - functionExpressionPath.has("id")
    + functionExpressionPath.node.id

    - functionExpressionPath.has("arguments")
    + !!functionExpressionPath.node.arguments.length

    - functionExpressionPath.isnt("async")
    + !functionExpressionPath.node.async

low

  • Remove block argument from Scope#rename (#15288)

    - rename(oldName: string, newName?: string, block?: t.Pattern | t.Scopable)
    + rename(oldName: string, newName?: string)

    Migration: In Babel 8 the third argument block is not used by the method. Consider remove it if you are depending on Scope#rename.

  • Allow skipped NodePaths to be requeued (#13291)

    Notes: NodePath#requeue() can requeue a skipped NodePath. This is actually a bugfix, but it causes an infinite loop in the tdz implementation of @babel/plugin-transform-block-scoping in Babel 7. So it may break other plugins as well.

    Migration: Adapt to the new behaviour. You can use NodePath#shouldSkip to check whether a NodePath has been skipped before calling NodePath#requeue().

  • Remove methods starting with _ (#16504)

    _assertUnremoved
    _call
    _callRemovalHooks
    _containerInsert
    _containerInsertAfter
    _containerInsertBefore
    _getKey
    _getPattern
    _getQueueContexts
    _getTypeAnnotation
    _markRemoved
    _remove
    _removeFromScope
    _replaceWith
    _resolve
    _resyncKey
    _resyncList
    _resyncParent
    _resyncRemoved
    _verifyNodeList

    Migration: These methods are meant to be private so there is no real migration approach. But if your plugin / build is broken by this change, feel free to open an issue and tell us how you use these methods and we can see what we can do after Babel 8 is released.

@babel/compat-data

low

  • Remove ios_saf from data/native-modules.json (#15068)

    Migration: Use ios instead.

  • Rename stage 4 plugin entries from proposal-* to transform-* in plugins.json (#14976)

    Migration: For example, use transform-class-properties rather than proposal-class-properties. For a complete list of renamed plugin, see Packages Renames section of Babel 8 migration.

@babel/eslint-plugin

low

  • The default named exports has been removed (#14180)

    Migration: This change has no effect if you are using the plugin in your ESLint config. However, if you are extending @babel/eslint-plugin, ensure that you obtain exports from require("@babel/eslint-plugin") rather than require("@babel/eslint-plugin").default.

    my-eslint-plugin.cjs
    // Don't add `.default` after `require()`
    const { rules, rulesConfig } = require("@babel/eslint-plugin")

@babel/helper-compilation-targets

low

  • isRequired will not accept renamed proposal- queries (#14976)

    my-babel-plugin.js
    module.exports = api => {
    const targets = api.targets();
    // The targets have native optional chaining support
    // if `transform-optional-chaining` is _not_ required
    const optionalChainingSupported = !isRequired(
    - "proposal-optional-chaining",
    + "transform-optional-chaining",
    targets
    );
    };

    Migration: Use the transform-* plugin name if the plugin is listed in the Packages Renames section of Babel 8 migration.

@babel/helper-replace-supers

low

  • Remove named export environmentVisitor (#15550)

    Migration: Import it from @babel/helper-environment-visitor.

    - import { environmentVisitor } from "@babel/helper-replace-supers";
    + import environmentVisitor from `@babel/helper-environment-visitor`;
  • Remove named export skipAllButComputedKey (#15550)

    Migration: Use requeueComputedKeyAndDecorators instead. Find and replace the following import and usage

    my-babel7-plugin.js
    import { skipAllButComputedKey } from "@babel/helper-replace-supers";
    skipAllButComputedKey(path);

    to

    my-babel8-plugin.js
    import { requeueComputedKeyAndDecorators } from `@babel/helper-environment-visitor`;
    path.skip();
    requeueComputedKeyAndDecorators(path);

@babel/helper-simple-access

low

  • Remove the the third parameter includeUpdateExpression from the default export (#15550)

    This change probabaly won't break your integration as includeUpdateExpression defaults to true in Babel 7. If you are using includeUpdateExpression: false, adapt to the new behaviour.

@babel/highlight

low

Plugin changes

medium

  • Remove getModuleName from plugin pass (#12724)

    Migration: Use .file.getModuleName instead. For example,

    my-babel-plugin.js
    module.exports = {
    name: "my-babel-plugin",
    visitor: {
    Identifier(path, pass) {
    - const moduleName = pass.getModuleName();
    + const moduleName = pass.file.getModuleName();
    }
    }
    }

low

  • Remove addImport from plugin pass (#15576)

    Migration: This change probably will not affect your plugin as this method is already throwing an error in Babel 7. Use addNamed or addDefault from @babel/helper-module-imports instead.

  • Stop supporting fields as named exports (#15576)

    Migration: This change disallows plugins declared from named exports, for example,

    legacy-babel-plugin.js
    exports.name = "legacy-babel-plugin";
    exports.visitor = {
    Identifier() {}
    }

    find such patterns and migrate to the following patterns.

    my-babel-plugin.cjs
    module.exports = {
    name: "babel-plugin",
    visitor: {
    Identifier() {}
    }
    }