This is the 13th day of my participation in the August More Text Challenge.More challenges in August

preface

In the old project, I encountered an error in compiling Babel import syntax. Through the final analysis, I located the problem. The following is a record of how to solve the problem.

Configure the environment

Take a look at the configuration file first. The non-important content has been filtered.

// .babelrc
{
  presets: ['react-native']
}
  


// package.json
{
  dependencies: {
    "react-native": "0.44.0"
  },
  devDependencies: {
    "babel-core": "6.26.3"."babel-preset-react-native": "4.0.0"}}Copy the code

Minimum reproduction demo

import styles from './selectFormItemStyles'


class SelectFormItem {

  // a(styles) {} will compile properly
  a(styles = {}) {}


  render() {
    // This styles is not compiled correctly
    styles.dxz
  }
}
  
Copy the code

The result of compiling the above code is as follows:

// ignore nouse code ...
var _selectFormItemStyles = require('./selectFormItemStyles');
var _selectFormItemStyles2 = _interopRequireDefault(_selectFormItemStyles); 


var SelectFormItem = function () {


_createClass(SelectFormItem, [{
  key: 'a'.value: function a() {
    var styles = arguments.length > 0 && arguments[0]! = =undefined ? arguments[0] : {}; }}, {key: 'render'.value: function render() { styles.dxz; }}]);returnSelectFormItem; } ();Copy the code

You can see the compiled, styles in the render function. DXZ hasn’t been amended as _selectFormItemStyles2. DXZ

Cause analysis,

A styles. DXZ compilation error could be caused by the default parameter values in function A causing the import syntax to compile without replacing the styles reference in render correctly. Since only the babel-react-native “: “@4.0.0” plugin is used, and this plugin is a set of Babel plugins that Facebook has encapsulated, consider strip out the plugins actually used in the current compilation error environment and locate the faulty plugin.

Demo after stripping:

const babel = require("babel-core")


const { code: res } = babel.transform(code, {
  plugins: [
    'transform-es2015-block-scoping'.'transform-es2015-parameters'['transform-es2015-modules-commonjs',
      { strict: false.allowTopLevelThis: true}].'transform-es2015-classes',]})Copy the code

The four plugins above are used in the current demo:

  • transform-es2015-block-scoping

Convert let/const to var

  • transform-es2015-parameters

Convert function default parameters to arguments within the function

  • transform-es2015-modules-commonjs

Convert the import syntax to require

  • transform-es2015-classes

Convert the class syntax to Protopype

After the test, it is found that when only transform-ES2015-modules-commonJS plug-in is enabled for compilation, the compilation result is normal.

See how transform-es2015-modules-commonJS compiles the import syntax.

// ignore nouse code
exports.default = function() {
  return {
    Program: {


      // Do import syntax compilation after all other plug-ins are compiled
      exit(path){
        const imports = {}
        const body = path.get('body')
        for (let i = 0; i < body.length; i++) {
          const _path = body[i]


          // Find the import syntax
          if (_path.isImportDeclaration()) {
            const key = _path.node.source.value


            // Save the import information to imports
            imports[key] = path.node /* general meaning */


            // Remove the import syntax
            _path.remove()
          }
        }


        // Iterate over the collected imports node and replace it with require syntax
        for (let source in imports) {
          buildRequire(t.stringLiteral(source))
        }


        // Iterate over the resource name referenced to import in all nodes and replace it with a name
        path.traverse({
          AssignmentExpression(path) {
            const left = path.get('left')
            if (left.isIdentifier()) {
              const name = left.node.name


              // Call babel-traverse's getBinding to check if the current resource name is in the same scope as the import resource name
              if (this.scope.getBinding(name) ! == path.scope.getBinding(name))return
            } else {


              // ignore other branches analysis}}})}}}}Copy the code

Summarize the previous conclusions and the plug-in source code findings

  • Used alonetransform-es2015-modules-commonjsCompile without error
  • The resource name that references the import declaration in the code is passedbabel-traverse 的 getBindingjudgemental
  • The plug-in is executed last

At this point it is assumed that the other three plug-ins caused the babel-traverse getBinding (which gets the scope of the current variable binding) to fail.

Due to the large number of Babel-traverse codes, consider starting with three plug-ins to look for code that might cause scope changes. Transform-es2015-block-scoping: transform-es2015-block-scoping: transform-es2015-block-scoping

// ignore unuse code
function convertBlockScopedToVar(path, node, parent, scope) {


  // Whether the scope needs to be moved to the parent
  const moveBindingsToParent = arguments.length > 4&&...if (moveBindingsToParent) {


    // Get the scope of the parent function
    var parentScope = scope.getFunctionParent();


    // Get the variable that needs to be replaced referencing the resource name
    var ids = path.getBindingIdentifiers();
    for (var name in ids) {


      // Get its own binding
      var binding = scope.getOwnBinding(name);
      if (binding) binding.kind = "var";


      // Move the scope of the current variable to the parentscope.moveBindingTo(name, parentScope); }}}Copy the code

From the above code:

  • And are referenced toimportExecute a variable with the same resource namebabel-traverseIn themoveBindingToMethod to raise the scope of the variable

The OwnBinding for styles. DXZ variable is missing if the command output is similar to the following:

After analysis, it is assumed that when OwnBingding does not exist, this problem can be solved by not carrying out the operation of moveBingingTo enhancing scope.

The solution

// [email protected]/lib/index.js#L132 for (var name in ids) {var binding = scope.getOwnBinding(name); if (binding) binding.kind = "var";+ if (binding) {
     scope.moveBindingTo(name, parentScope);
+}
}
  
Copy the code

Modified found styles. DXZ by converting the correct _selectFormItemStyles2. Default. DXZ

conclusion

The analysis process

The above analysis does not have a complete view of babel-traverse and other plugins, any errors are welcome.

Other options

As the project has not been maintained for a long time, the 6.x version of Babel is still used. After testing, it is found that babel-react-native is upgraded to 5.x version and Babel needs 7.x version. After upgrading Babel, the problem is found to be cleared.