packages/babel-core/src/parser/util/missing-plugin-helper.js

Summary

Maintainability
A
3 hrs
Test Coverage
// @flow

const pluginNameMap = {
  classProperties: {
    syntax: {
      name: "@babel/plugin-syntax-class-properties",
      url: "https://git.io/vb4yQ",
    },
    transform: {
      name: "@babel/plugin-proposal-class-properties",
      url: "https://git.io/vb4SL",
    },
  },
  classPrivateProperties: {
    syntax: {
      name: "@babel/plugin-syntax-class-properties",
      url: "https://git.io/vb4yQ",
    },
    transform: {
      name: "@babel/plugin-proposal-class-properties",
      url: "https://git.io/vb4SL",
    },
  },
  classPrivateMethods: {
    syntax: {
      name: "@babel/plugin-syntax-class-properties",
      url: "https://git.io/vb4yQ",
    },
    transform: {
      name: "@babel/plugin-proposal-private-methods",
      url: "https://git.io/JvpRG",
    },
  },
  decorators: {
    syntax: {
      name: "@babel/plugin-syntax-decorators",
      url: "https://git.io/vb4y9",
    },
    transform: {
      name: "@babel/plugin-proposal-decorators",
      url: "https://git.io/vb4ST",
    },
  },
  doExpressions: {
    syntax: {
      name: "@babel/plugin-syntax-do-expressions",
      url: "https://git.io/vb4yh",
    },
    transform: {
      name: "@babel/plugin-proposal-do-expressions",
      url: "https://git.io/vb4S3",
    },
  },
  dynamicImport: {
    syntax: {
      name: "@babel/plugin-syntax-dynamic-import",
      url: "https://git.io/vb4Sv",
    },
  },
  exportDefaultFrom: {
    syntax: {
      name: "@babel/plugin-syntax-export-default-from",
      url: "https://git.io/vb4SO",
    },
    transform: {
      name: "@babel/plugin-proposal-export-default-from",
      url: "https://git.io/vb4yH",
    },
  },
  exportNamespaceFrom: {
    syntax: {
      name: "@babel/plugin-syntax-export-namespace-from",
      url: "https://git.io/vb4Sf",
    },
    transform: {
      name: "@babel/plugin-proposal-export-namespace-from",
      url: "https://git.io/vb4SG",
    },
  },
  flow: {
    syntax: {
      name: "@babel/plugin-syntax-flow",
      url: "https://git.io/vb4yb",
    },
    transform: {
      name: "@babel/preset-flow",
      url: "https://git.io/JfeDn",
    },
  },
  functionBind: {
    syntax: {
      name: "@babel/plugin-syntax-function-bind",
      url: "https://git.io/vb4y7",
    },
    transform: {
      name: "@babel/plugin-proposal-function-bind",
      url: "https://git.io/vb4St",
    },
  },
  functionSent: {
    syntax: {
      name: "@babel/plugin-syntax-function-sent",
      url: "https://git.io/vb4yN",
    },
    transform: {
      name: "@babel/plugin-proposal-function-sent",
      url: "https://git.io/vb4SZ",
    },
  },
  importMeta: {
    syntax: {
      name: "@babel/plugin-syntax-import-meta",
      url: "https://git.io/vbKK6",
    },
  },
  jsx: {
    syntax: {
      name: "@babel/plugin-syntax-jsx",
      url: "https://git.io/vb4yA",
    },
    transform: {
      name: "@babel/preset-react",
      url: "https://git.io/JfeDR",
    },
  },
  logicalAssignment: {
    syntax: {
      name: "@babel/plugin-syntax-logical-assignment-operators",
      url: "https://git.io/vAlBp",
    },
    transform: {
      name: "@babel/plugin-proposal-logical-assignment-operators",
      url: "https://git.io/vAlRe",
    },
  },
  moduleAttributes: {
    syntax: {
      name: "@babel/plugin-syntax-module-attributes",
      url: "https://git.io/JfK3k",
    },
  },
  numericSeparator: {
    syntax: {
      name: "@babel/plugin-syntax-numeric-separator",
      url: "https://git.io/vb4Sq",
    },
    transform: {
      name: "@babel/plugin-proposal-numeric-separator",
      url: "https://git.io/vb4yS",
    },
  },
  optionalChaining: {
    syntax: {
      name: "@babel/plugin-syntax-optional-chaining",
      url: "https://git.io/vb4Sc",
    },
    transform: {
      name: "@babel/plugin-proposal-optional-chaining",
      url: "https://git.io/vb4Sk",
    },
  },
  pipelineOperator: {
    syntax: {
      name: "@babel/plugin-syntax-pipeline-operator",
      url: "https://git.io/vb4yj",
    },
    transform: {
      name: "@babel/plugin-proposal-pipeline-operator",
      url: "https://git.io/vb4SU",
    },
  },
  privateIn: {
    syntax: {
      name: "@babel/plugin-syntax-private-property-in-object",
      url: "https://git.io/JfK3q",
    },
    transform: {
      name: "@babel/plugin-proposal-private-property-in-object",
      url: "https://git.io/JfK3O",
    },
  },
  recordAndTuple: {
    syntax: {
      name: "@babel/plugin-syntax-record-and-tuple",
      url: "https://git.io/JvKp3",
    },
  },
  throwExpressions: {
    syntax: {
      name: "@babel/plugin-syntax-throw-expressions",
      url: "https://git.io/vb4SJ",
    },
    transform: {
      name: "@babel/plugin-proposal-throw-expressions",
      url: "https://git.io/vb4yF",
    },
  },
  typescript: {
    syntax: {
      name: "@babel/plugin-syntax-typescript",
      url: "https://git.io/vb4SC",
    },
    transform: {
      name: "@babel/preset-typescript",
      url: "https://git.io/JfeDz",
    },
  },

  // TODO: This plugins are now supported by default by @babel/parser: they can
  // be removed from this list. Although removing them isn't a breaking change,
  // it's better to keep a nice error message for users using older versions of
  // the parser. They can be removed in Babel 8.
  asyncGenerators: {
    syntax: {
      name: "@babel/plugin-syntax-async-generators",
      url: "https://git.io/vb4SY",
    },
    transform: {
      name: "@babel/plugin-proposal-async-generator-functions",
      url: "https://git.io/vb4yp",
    },
  },
  nullishCoalescingOperator: {
    syntax: {
      name: "@babel/plugin-syntax-nullish-coalescing-operator",
      url: "https://git.io/vb4yx",
    },
    transform: {
      name: "@babel/plugin-proposal-nullish-coalescing-operator",
      url: "https://git.io/vb4Se",
    },
  },
  objectRestSpread: {
    syntax: {
      name: "@babel/plugin-syntax-object-rest-spread",
      url: "https://git.io/vb4y5",
    },
    transform: {
      name: "@babel/plugin-proposal-object-rest-spread",
      url: "https://git.io/vb4Ss",
    },
  },
  optionalCatchBinding: {
    syntax: {
      name: "@babel/plugin-syntax-optional-catch-binding",
      url: "https://git.io/vb4Sn",
    },
    transform: {
      name: "@babel/plugin-proposal-optional-catch-binding",
      url: "https://git.io/vb4SI",
    },
  },
};

//todo: we don't have plugin-syntax-private-property-in-object
pluginNameMap.privateIn.syntax = pluginNameMap.privateIn.transform;

const getNameURLCombination = ({ name, url }) => `${name} (${url})`;

/*
Returns a string of the format:
Support for the experimental syntax [@babel/parser plugin name] isn't currently enabled ([loc]):

[code frame]

Add [npm package name] ([url]) to the 'plugins' section of your Babel config
to enable [parsing|transformation].
*/
export default function generateMissingPluginMessage(
  missingPluginName: string,
  loc: { line: number, column: number },
  codeFrame: string,
): string {
  let helpMessage =
    `Support for the experimental syntax '${missingPluginName}' isn't currently enabled ` +
    `(${loc.line}:${loc.column + 1}):\n\n` +
    codeFrame;
  const pluginInfo = pluginNameMap[missingPluginName];
  if (pluginInfo) {
    const { syntax: syntaxPlugin, transform: transformPlugin } = pluginInfo;
    if (syntaxPlugin) {
      const syntaxPluginInfo = getNameURLCombination(syntaxPlugin);
      if (transformPlugin) {
        const transformPluginInfo = getNameURLCombination(transformPlugin);
        const sectionType = transformPlugin.name.startsWith("@babel/plugin")
          ? "plugins"
          : "presets";
        helpMessage += `\n\nAdd ${transformPluginInfo} to the '${sectionType}' section of your Babel config to enable transformation.
If you want to leave it as-is, add ${syntaxPluginInfo} to the 'plugins' section to enable parsing.`;
      } else {
        helpMessage +=
          `\n\nAdd ${syntaxPluginInfo} to the 'plugins' section of your Babel config ` +
          `to enable parsing.`;
      }
    }
  }
  return helpMessage;
}