directory

  • preface
    • What is the Babel
    • What can Babel do
  • The working process
    • Parsing the Parse
    • Transformation Transform
    • The Generator is generated
  • use
    • babel-standalone
      • introduce
      • version
      • The sample
      • Question and answer
      • supplement
    • Cli command
      • introduce
      • The installation
      • use
      • Question and answer
      • supplement
    • Plug-ins as build tools
  • The configuration file
    • First, use the API
      • introduce
      • The sample
      • Question and answer
    • 2. Use the CLI
      • introduce
      • The sample
      • Question and answer
    • Three, babelrc
      • introduce
      • The sample
      • Question and answer
      • supplement
    • Four, Babel. Config. Json
      • introduce
      • The sample
      • supplement
  • Module is introduced
    • babel-node
  • Version update
  • Question and answer
  • conclusion

preface

What is the Babel

Babel is a JavaScript compiler

This is the short but precise definition of “Babel.” What does it mean? Define it first as a compiler, and second as solely responsible for the JavaScript language.

See wikipedia for compiler concepts at bk.tw.lvfukeji.com/wiki/%E7%BC…

What can Babel do

Here we just need to know how Babel works as a JavaScript compiler: parse -> transform -> generate.

In common parlour, Babel is responsible for converting JavaScript’s high-level syntax and new apis into lower-level specifications to ensure that they can be executed in lower-level environments. Here’s how Babel works.

The working process

Parsing the Parse

All things are difficult at the beginning. Who should do the first step of parsing?

babylon

Babylon is a JavaScript parser.

Babel itself is not responsible for parsing, but instead calls the babible.parse method to parse the source code to generate an AST tree.

Transformation Transform

babel-traverse

Babel-traverse traverses the AST tree for additions, deletions, and changes.

After retrieving the AST tree from the first step, update the tree by calling the traverse method provided by the Babel-traverse library.

babel-types

An AST tree-based tool library (add, delete, change and check nodes). Babel-traverse uses this library when traversing AST trees.

The Generator is generated

The last step is to update the AST tree to generate the code.

babel-generator

The provider method receives the AST parameter and returns the changed source code.

This is a general description of the Babel compiler process. So Babel is a series of actions.

use

babel-standalone

introduce

As Babel runs on a Node environment, for non-Node environments such as browsers, the babel-standalone open source project provides babel.min.js that can be imported and used via

Standalone Babel-standalone has joined the Babel family as a standalone and will be available for download in the 7.x Babel pack.

version
The name of the version volume note Online address
babel.js 6.26.0 1.78 MB uncompressed Unpkg.com/babel-stand…
babel.min.js 6.26.0 773KB compressed Unpkg.com/babel-stand…
babel.js 7.12.9 3.1 MB uncompressed unpkg.com/@babel/stan…
babel.min.js 7.12.9 1.6 MB compressed unpkg.com/@babel/stan…
The sample
  • Example 1 ES6 transcoding
    • use<script>Import online address or local reference after download.
    • Put the ES6 code you wrote in<script type="text/babel">Inside, one thing needs to be notedtypeA type oftext/babel.
<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - standalone es6 transcoding</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script type="text/babel">
      const getMsg = () = > {
        const name = 'Babel';
        document.getElementById(
          'output'
        ).innerHTML = `Hello ${name} version:${Babel.version}`;
      };
      getMsg();
    </script>
  </body>
</html>
Copy the code

  • Example 2 Simulates the script entered by users in online real-time transcoding

    • This method is applicable to some online transcoding scenarios:

      • Babel homepage

      • Babel-repl online transcoding

      • JSFiddle

      • JSBin

These all introduce babel.min.js by calling various apis provided by Babel objects (e.g. Transform, disableScriptTags, transformScriptTags…). Realize online real-time transcoding.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Standalone simulates a script for online real-time transcoding user input</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>Input:<textarea id="input" style="width: 100%" rows="15">Class UserInfo{constructor(name=' zhang3 ') {this.name = name; } getUserName(){ return `${this.name}`; }} const user=new UserInfo; console.log(user.getUserName());</textarea>Real-time transcoding:<pre id="output"></pre>

    <script>
      var inputEl = document.getElementById('input');
      var outputEl = document.getElementById('output');

      function transform() {
        try {
          outputEl.innerHTML = Babel.transform(inputEl.value, {
            presets: [
              'es2015'['stage-2',
                {
                  decoratorsBeforeExport: false,
                },
              ],
            ],
          }).code;
        } catch (e) {
          outputEl.innerHTML = 'ERROR: ' + e.message;
        }
      }

      inputEl.addEventListener('keyup', transform, false);
      transform();
    </script>
  </body>
</html>
Copy the code

  • Example 3: Use import and export

All of the above examples write ES6 code directly on the page in an inline way, but in real development you might need to introduce scripts in an out-of-chain way, so let’s see what the problems are.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script type="text/babel" src="./index.js"></script>
  </body>
</html>
Copy the code
// index.js
const getMsg = () = > {
  const name = 'Babel';
  document.getElementById(
    'output'
  ).innerHTML = `Hello ${name} version:${Babel.version}`;
};
getMsg();
Copy the code

Changing example 1 from inline to exchaining to introduce scripts proved to be fine. But instead of just writing an index.js script, do es6 syntaxes like import and export also support it?

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script type="text/babel">
      export default {
        name: 'Little friend'.age: 18};</script>
  </body>
</html>
Copy the code

Uncaught ReferenceError: Uncaught ReferenceError: Exports is not defined, es6 exports is not defined, es6 exports is not defined, es6 exports is not defined, es6 exports is not defined, ES6 exports is not defined, es6 exports is not defined, es6 exports is not defined The babel-standalone project provides babel.min.js, which contains all the plug-ins (features, specifications) that Babel uses and can be configured in UMD mode.

6. Configure the X versiondata-plugins="transform-es2015-modules-umd"

7. Configure the X versiondata-plugins="transform-modules-umd"

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script type="text/babel" data-plugins="transform-modules-umd">
      export default {
        name: 'Little friend'.age: 18};</script>
  </body>
</html>
Copy the code

Now that you can define export exports, how do you import imports? Here, babel-standalone also provides us with the data-Module that defines the name of the module to export, which is then imported.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      data-module="userInfo"
    >
      export default {
        name: 'Little friend'.age: 18};</script>
    <script type="text/babel" data-plugins="transform-modules-umd">
      import userInfo from 'userInfo';
      document.getElementById('output').innerHTML = `Hello ${userInfo.name}`;
    </script>
  </body>
</html>
Copy the code

This a few seem to have not found a problem, really have no what problem? Then directly modify the way to the outer chain.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./userInfo.js"
    ></script>

    <script type="text/babel" data-plugins="transform-modules-umd">
      import userInfo from 'userInfo';
      document.getElementById('output').innerHTML = `Hello ${userInfo.name}`;
    </script>
  </body>
</html>
Copy the code
// userInfo.js
export default {
  name: 'Little friend'.age: 18};Copy the code

If you look at the above code, do you find that the script tag lacks the attribute data-module to define the module name, but it still works?

To highlight,babel.min.jsInternal accessscriptThe tag then makes a judgment on the property, if anysrcAttribute uses the attribute value as the module name (e.gsrc="./userInfo.js"With a finaluserInfoAs a module name), if notsrcProperty getsdata-moduleProperty value as the module name, so this is not required if you use the outer chaindata-moduleProperty (invalid even if configured).

The above example shows how export can be used as an external chain, but how can import be used as an external chain?

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./userInfo.js"
    ></script>

    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./index.js"
    ></script>
  </body>
</html>
Copy the code
// userInfo.js
export default {
  name: 'Little friend'.age: 18};Copy the code
// index.js
import userInfo from 'userInfo';
document.getElementById('output').innerHTML = `Hello ${userInfo.name}`;
Copy the code

Import is used in the same way as export, thus completing the exchaining of the script.

Question and answer

I feel that there is something wrong with the way of modifying external chain of import export in the example part, and there is no pit at the end?

Hold on. So let’s go to the Q&A and talk alone…

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./index.js"
    ></script>
  </body>
</html>
Copy the code
// index.js
import userInfo from 'userInfo';
document.getElementById('output').innerHTML = `Hello ${userInfo.name}`;
Copy the code

As in the example above, import index.js directly and raise Uncaught TypeError: Cannot read property ‘name’ of undefined error because userInfo is undefined

So let’s start with transcoding.

// index.js after transcoding
(function (global, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['userInfo'], factory);
  } else if (typeof exports! = ='undefined') {
    factory(require('userInfo'));
  } else {
    var mod = {
      exports: {}}; factory(global.userInfo);
    global.index = mod.exports;
  }
})(
  typeofglobalThis ! = ='undefined'
    ? globalThis
    : typeofself ! = ='undefined'
    ? self
    : this.function (_userInfo) {
    'use strict';

    _userInfo = _interopRequireDefault(_userInfo);

    function _interopRequireDefault(obj) {
      return obj && obj.__esModule ? obj : { default: obj };
    }

    document.getElementById('output').innerHTML = 'Hello '.concat(
      _userInfo['default'].name ); });Copy the code

Let’s compare index.js transcoding before and after:

Before the transcoding After the transcoding
import userInfo from 'userInfo'; _userInfo = _interopRequireDefault(_userInfo);

function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj }; }

We see the transcoding code reassigning _userInfo using the _interopRequireDefault(_userInfo) function (_interopRequireDefault determines whether the default attribute is required).

Underline: why is there a paragraph inserted here__esModuleanddefaultProperty, as mentioned at the beginning of this section, since Babel runs in a Node environment, it is intended to convert ES6 modules to CommonJS.

export The import
es6 export

export default
import {}

import
CommonJS module.exports require

By comparison, it is found that es6 export and import can be in various forms, while CommonJS is a single object export and import. So Babel needs to make some minor changes to convert ES6 to CommonJS.

About module object addition__esModuleProperty to mark whether the module is transcoded, and if it is, the module is called directly (exports)defaultProperty export object (Babel will export es6export defaultThe default transcoding of exported objects isexports.defaultAt the same time, this writing method conforms to CommonJS specificationmodule.exports = exports.default), mainly to achieve es6 transcoding and CommonJS specification consistency.

aboutdefaultProperties, as described above are available__esModuleAttribute case if no__esModuleAttribute Description The module was not transformed (possibly a third party module) in this case the module is called directly (exports)defaultProperties forundefined, so this case just returns an object and adds one to that objectdefaultAttribute, pointing the value of the attribute to itself (as in the transcoding above)return obj && obj.__esModule ? obj : { default: obj })

Wake up, these two properties are not the point here, remember what the problem was? UserInfo = userInfo; userInfo = userInfo; userInfo = userInfo; userInfo = userInfo; userInfo = userInfo; Find out where userInfo is defined…

If you look at the code after transcoding, there is an if… else if… Else make judgment on various environments (AMD, CommonJS, UMD) since we are executing in browser (UMD) There is a userInfo on the global.userinfo global object, so we can get the following result: import object reference directly from the script imported in the external link mode, without any declaration export, this object is undefined(actually in AMD) Define ([‘userInfo’], factory), CommonJS require(‘userInfo’).

In a word: no matter what form of reference, must be declared, so the external chain is still neededscriptThe statement.

In a word: no matter what form of reference, must be declared, so the external chain is still neededscriptThe statement.

In a word: no matter what form of reference, must be declared, so the external chain is still neededscriptThe statement.

The statement used here may not be exactly worded, but it should make sense.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel - Use of standalone import, export</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./userInfo.js"
    ></script>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./index.js"
    ></script>
  </body>
</html>
Copy the code
// userInfo.js
export default {
  name: 'Little friend'.age: 18};Copy the code
// index.js
import userInfo from 'userInfo';
document.getElementById('output').innerHTML = `Hello ${userInfo.name}`;
Copy the code
// userinfo.js after transcoding
(function (global, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['exports'], factory);
  } else if (typeof exports! = ='undefined') {
    factory(exports);
  } else {
    var mod = {
      exports: {}}; factory(mod.exports);global.userInfo = mod.exports;
  }
})(
  typeofglobalThis ! = ='undefined'
    ? globalThis
    : typeofself ! = ='undefined'
    ? self
    : this.function (_exports) {
    'use strict';

    Object.defineProperty(_exports, '__esModule', {
      value: true}); _exports['default'] = void 0;
    var _default = {
      name: 'Little friend'.age: 18}; _exports['default'] = _default; });Copy the code
// index.js after transcoding
(function (global, factory) {
  if (typeof define === 'function' && define.amd) {
    define(['userInfo'], factory);
  } else if (typeof exports! = ='undefined') {
    factory(require('userInfo'));
  } else {
    var mod = {
      exports: {}}; factory(global.userInfo);
    global.index = mod.exports;
  }
})(
  typeofglobalThis ! = ='undefined'
    ? globalThis
    : typeofself ! = ='undefined'
    ? self
    : this.function (_userInfo) {
    'use strict';

    _userInfo = _interopRequireDefault(_userInfo);

    function _interopRequireDefault(obj) {
      return obj && obj.__esModule ? obj : { default: obj };
    }

    document.getElementById('output').innerHTML = 'Hello '.concat(
      _userInfo['default'].name ); });Copy the code

Following the previous question, if the external chain introduces userinfo.js but does not import userInfo from ‘userInfo’ in index.js; So what?

For this question, please note that the __esModule and default attributes mentioned above are not the focus here, but the answer to this question.

Babel-standalone how to use multiple import export.

In fact, it is the same as the above example, just need to chain multiple scripts.

<! DOCTYPEhtml>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Babel-standalone How to use multiple imports, exports</title>
    <script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
  </head>
  <body>
    <div id="output"></div>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./other.js"
    ></script>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./userInfo.js"
    ></script>
    <script
      type="text/babel"
      data-plugins="transform-modules-umd"
      src="./index.js"
    ></script>
  </body>
</html>
Copy the code
// other.js
export function random() {
  return Math.floor(Math.random() * 10);
}

export const randomStr = 'Lucky number :';
Copy the code
// userInfo.js
export default {
  name: 'Little friend'.age: 18};Copy the code
// index.js
import { randomStr, random } from 'other';
import userInfo from 'userInfo';
function init() {
  document.getElementById('output').innerHTML = `Hello ${ userInfo.name } ${randomStr} ${random()}`;
}

init();
Copy the code
supplement

It is convenient for developers to directly write es6 code in real time transcoding, but it also takes time and affects performance. Therefore, it can be used in development environments. For production environments, it is recommended to load the script after transcoding.

You are using the in-browser Babel Transformer. Be sure to precompile your scripts for production.

Cli command

introduce

CLI: The command-line Interface (CLI) was the most widely used user Interface before the graphical user Interface (GUI) was popularized. It usually does not support a mouse. Users input commands through the keyboard, and the computer executes the commands after receiving them.

Concept of CLI wikipedia bk.tw.lvfukeji.com/wiki/CLI for reference

Babel built-in CLI, can be directly in the command line transcoding files.

The installation

Before installing the Babel CLI, check whether node and NPM are installed.

The Babel CLI is installed in the following two modes:

  1. Global installation: NPM install –global Babel – CLI Projects need to rely on global installation, and if multiple projects rely on different versions, it cannot be resolved…

  2. NPM install –save-dev Babel -cli Install directly in the project, completely solve the problem of global installation.

Partial installation is recommended, and the following examples are partial installation.

use

Before introducing use, prepare the project environment.

  1. Create the project.
  2. It must be included in the projectpackage.jsonFile (create your own)

About the package. The json related configuration may refer to docs.npmjs.com/cli/v6/conf…

Enter the use of the Babel CLI.

The project directory structure is as follows:

| - Babel - cli project name | -- package. JsonCopy the code
// package.json
{
  "name": "cli"
}
Copy the code
  1. Use the command to enter the projectcd babel-cli
  2. Run partial installation commandsnpm install --save-dev babel-cli

After installation, the project directory structure is as follows:

| - Babel - cli project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node dependent module set | - Babel - cli cli module | - Babel - * begin with Babel will introduce the various modules of behindCopy the code
// package.json
{
  "name": "cli"."devDependencies": {
    "babel-cli": "^ 6.26.0"}}Copy the code

Now that all configuration and installation is complete, we create a script file (script.js) in the project and perform transcoding.

| - Babel - cli project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node dependent module set | - Babel - cli cli module | - Babel - * begin with Babel behind each module will introduce | -- script. Js script fileCopy the code
// script.js
const hello = 'Hello';

[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

Run commands to transcode…

  • Transcoding output to STDout (standard output).

npx babel script.js

  • Transcoding output to the specified file.

npx babel script.js --out-file script-compiled.js

  • Transcoding a directory to a specified directory

npx babel src --out-dir lib

Above is about Babel cli is simple to use, more configuration may refer to website www.babeljs.cn/docs/babel-…

Question and answer

What is the difference between global installation, local installation, NPX and NPM?

Babel CLI has two types of installation (global installation, local installation). As mentioned above, there are also two types of command transcoding after installation:

  1. After global installation, directly execute Babel command such as (Babel script.js), which is to find global node_modules/ Babel -cli/bin/babel.js execution.

  2. Node_modules /. Bin/Babel -cli/bin/babel.js

The only difference between global and local is that the search path is different.

The only difference between global and local is that the search path is different.

The only difference between global and local is that the search path is different.

Next, compare NPX and NPM.

Run you want to use NPM transcoding requires the configuration package. The json object of scripts, about related configuration scripts docs.npmjs.com/cli/v6/comm for reference…

// package.json
{
  "name": "cli"."devDependencies": {
    "babel-cli": "^ 6.26.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"}}Copy the code

As shown in the preceding example, you can configure the scripts object of package.json, go to the project directory, and run the NPM run Babel and NPM run babel-compiled commands to achieve transcoding.

Can’t NPM be used without configuring scripts objects? Node_modules /.bin/ Babel, so we can manually call node_modules/.bin/ Babel script.js on the command line to achieve transcoding.

What can be done that doesn’t require the command line to type node_modules/… This is a bunch of paths, and you don’t need to configure scripts objects. The answer is NPX, which looks directly for node_modules/.bin in the project, without us having to manually type or configure the scripts object.

How do NPX and NPM find node_modules when global and local are installed?

Node_modules () : Node_modules () : node_modules () : node_modules ()

Why is the code output from the usage example above not transcoded?

/ / code before
const hello = 'Hello';

[1.2.3.4].forEach((item) = > console.log(item));
Copy the code
/ / after transcoding
const hello = 'Hello';

[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

This is just a primer on the Babel CLI, which will be covered later in transcoding (syntax, new features) and configuration files, and it is a good idea to use the Babel CLI if you want to develop your library.

supplement
  • aboutnpmHow to find the directorynode_modulesBag problem?
| - Babel - cli project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node dependent module set | - Babel - cli cli module | - Babel - * begin with Babel behind each module will introduce | -- script. Js script fileCopy the code
// package.json
{
  "name": "cli"."devDependencies": {
    "babel-cli": "^ 6.26.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"}}Copy the code

The directory structure of the example and some of the associated files are listed above, so the directory structure is relatively simple because it is just an example (all the files are in the project’s level 1 directory). Let’s use this example to see if we can find out how NPM retrievesnode_modules and package.json.

  1. Execute commands firstcd babel-cliGo to the project directory.
  2. Second to performnpm run babel.

Let’s break down the NPM Run Babel process to see what we can learn.

  • The first step is to start the NPM of the system installation (if you don’t know the installation location, please search for it, I used which NPM command to find the location).

    • Start the/usr/local/bin/npm, internal callnpm-cli.jsnpm.js./config/core.js'These processes are beside the point.
  • What is the current working directory (Node calls process. CWD to get the directory) , it is the current working directory.

    • Because I was at the terminal through the commandcdEntered the project, so the current directory isbabel-cli
  • Finally, since node_modules and package.json are both in the first level directory, we can parse package.json scripts and call Babel in node_modules.

Is this the end of it? An example is gone in a few sentences? So what if the current directory doesn’t have node_modules or package.json? What if there are multilevel directories? None of this is bullshit…

I don’t believe it.

Then look, first of all, modify the project structure as follows:

| - Babel - cli project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node dependent module set | - Babel - cli cli module | - Babel - * begin with Babel is behind the various modules of the introduces the class script file | - utils tool set | -- index. Js | -- script. Js script fileCopy the code
// utils/index.js
export const get = () = > {
  return 'get';
};
Copy the code

Utils /index.js was added to the directory.

// package.json
{
  "name": "config-babel-babelrc"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"."utils": "babel ./utils/index.js"}}Copy the code

Utils is added to the scripts object of package.json. Json and index.js are not in the same directory, so utils needs to be configured as a relative path to find the file “babel. /utils/index.js”.

The script.js file is identical to package.json so there is no need to configure a relative path (” Babel script.js”).

First let’s go to CD Babel -cli/utils and run NPM run utils to see what happens.

// Output the result
export const get = () = > {
  return 'get';
};
Copy the code

Ignore the code transcoding. Later, the configuration file describes how to configure transcoding.

Why didn’t the current directory utils node_modules, package. The json, instead of throwing and can be normal output fault, the above mentioned begins with a current directory search, did not mention the situation of the current directory search less than how to deal with, now encountered this kind of situation, we see the source code snippet directly do .

// npm/lib/config/core.js
Conf.prototype.loadPrefix = require('./load-prefix.js');
Copy the code
// npm/lib/config/load-prefix.js

// try to guess at a good node_modules location.
// If we are *explicitly* given a prefix on the cli, then
// always use that. otherwise, infer local prefix from cwd.
if (Object.prototype.hasOwnProperty.call(cli, 'prefix')) {
  p = path.resolve(cli.prefix);

  process.nextTick(cb);
} else {
  findPrefix(process.cwd()).then((found) = > {
    p = found;
    cb();
  }, cb);
}
Copy the code
// npm/node_modules/find-npm-prefix/find-prefix.js

function findPrefix(dir) {
  return new Promise((resolve, reject) = > {
    dir = path.resolve(dir);

    // this is a weird special case where an infinite recurse of
    // node_modules folders resolves to the level that contains the
    // very first node_modules folder
    let walkedUp = false;
    while (path.basename(dir) === 'node_modules') {
      dir = path.dirname(dir);
      walkedUp = true;
    }
    if (walkedUp) {
      resolve(dir);
    } else{ resolve(findPrefix_(dir)); }}); }function findPrefix_(dir, original) {
  if(! original) original = dir;const parent = path.dirname(dir);
  // this is a platform independent way of checking if we're in the root
  // directory
  if (parent === dir) return Promise.resolve(original);

  return new Promise((resolve, reject) = > {
    fs.readdir(dir, (err, files) = > {
      if (err) {
        // an error right away is a bad sign.
        // unless the prefix was simply a non
        // existent directory.
        if(err && dir === original && err.code ! = ='ENOENT') {
          reject(err);
        } else{ resolve(original); }}else if (
        files.indexOf('node_modules')! = = -1 ||
        files.indexOf('package.json')! = = -1
      ) {
        resolve(dir);
      } else{ resolve(findPrefix_(parent, original)); }}); }); }Copy the code

The three source code snippets above show how to retrieve locations. The findPrefix method in the find-prefix-.js file receives the dir parameter with the value process.cwd()(current directory), so you can see why it starts from the current directory The retrieval of node_modules or package.json files is stopped, otherwise it is retrieved to the parent directory.

So no matter in which directory NPM is executed… Either way, it always searches up the directory to find the packPage. json file (throw an error if the top-level directory doesn’t exist…). If the current directory has only node_modules or package.json, it will be thrown incorrectly because the source code is retrieved from one or the other. You need to explore for yourself.

  • npxAnd how to find the directorynode_modulesBag problem?

In fact, NPM is similar to the current directory from the start and up, so we directly look at the source code.

// npm/bin/npx-cli.js
const npx = require('libnpx');
const path = require('path');

const NPM_PATH = path.join(__dirname, 'npm-cli.js');

npx(npx.parseArgs(process.argv, NPM_PATH));
Copy the code
// npm/node_modules/libnpx/index.js
function localBinPath(cwd) {
  return require('./get-prefix.js')(cwd).then((prefix) = > {
    return prefix && path.join(prefix, 'node_modules'.'.bin');
  });
}
Copy the code
// npm/node_modules/libnpx/get-prefix.js
function getPrefix(root) {
  const original = (root = path.resolve(root));
  while (path.basename(root) === 'node_modules') {
    root = path.dirname(root);
  }
  if(original ! == root) {return Promise.resolve(root);
  } else {
    return Promise.resolve(getPrefixFromTree(root)); }}function getPrefixFromTree(current) {
  if (isRootPath(current, process.platform)) {
    return false;
  } else {
    return Promise.all([
      fileExists(path.join(current, 'package.json')),
      fileExists(path.join(current, 'node_modules')),
    ]).then((args) = > {
      const hasPkg = args[0];
      const hasModules = args[1];

      if (hasPkg || hasModules) {
        return current;
      } else {
        returngetPrefixFromTree(path.dirname(current)); }}); }}Copy the code

One thing to note about NPX is that if the current directory only has package.json, it will try to download the corresponding module to the current directory.

Plug-ins as build tools

introduce

Some build tools now integrate with Babel or provide configuration methods, but these plug-ins are configured differently, just like the Babel CLI. The final transcoding process is the same whether it is global or local (NPX, NPM), so the configuration and use of plug-ins are not covered here.

Webpack = > babel-loaderwww.webpackjs.com/loaders/bab…

Gulp = > gulp-babelwww.npmjs.com/package/gul…

A rollup = > rollup-plugin-babelgithub.com/rollup/roll…

The configuration file

Configuration has the following four ways of Babel’s website also has related introduction (www.babeljs.cn/docs/config)… .

First, use the API

introduce

This method is to transcode the code through the various apis provided by the Babel-core module. This method is introduced first because there are few usage scenarios.

The sample

The project directory structure is as follows:

| - config - Babel - API project name | -- package. JsonCopy the code
// package.json
{
  "name": "config-babel-api"
}
Copy the code
  1. Use the command to enter the projectcd config-babel-api
  2. Run partial installation commandsnpm install babel-core --save-dev

After installation, the project directory structure is as follows:

| - config - Babel - API project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node set | - rely on module Babel - core core module of Babel (bag) | - Babel - * begin with Babel will introduce the various modules of behindCopy the code
// package.json
{
  "name": "config-babel-api"."devDependencies": {
    "babel-core": "^ 6.26.3"}}Copy the code

Next we create a script file (script.js) in the project.

// script.js
[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

The code is relatively simple, with a forEach loop that prints the value of each item. The callback here uses the arrow function, which is transcoded in case some environments don’t support it.

Because the Babel – the core module provides transcoding API so we direct call, about more API reference website (www.babeljs.cn/docs/babel-)…

// transformbabel.transform(code: string, options? :Object.callback: Function)
Copy the code

The transform accepts three parameters (string (code that needs transcoding), configuration item, callback), so you also need to make the following changes to (script.js).

// script.js
const babel = require('babel-core');
const codeStr = '[1, 2, 3, 4].forEach((item) => console.log(item)); ';
const result = babel.transform(codeStr, {
  plugins: ['babel-plugin-transform-es2015-arrow-functions']});console.log('Output result :', result.code);
Copy the code
  • require('babel-core')Import modules.
  • Due to thetransformThe receiver is a string, so you need to convert the transcoding code to a string (the same operation if there are many codes (as a whole string)).
  • calltransform.
    • The first parameter takes a string variablecodeStr.
    • configurationplugins, transcoding the arrow function needs to downloadnpm install --save-dev babel-plugin-transform-es2015-arrow-functionsModules (packages).
// package.json
{
  "name": "config-babel-api"."devDependencies": {
    "babel-core": "^ 6.26.3"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"}}Copy the code

Finally, go to the CD config-babel-api directory where the script resides in the terminal or command window, and run the node command to run node script.js to view the output (the arrow function has been converted to a common function).

// Output result:
// [1, 2, 3, 4].forEach(function (item) {
// return console.log(item);
// });
Copy the code

This is how transcoding is implemented using the Babel API.

Question and answer

Can I transcode using Babel CLI scaffolding?

If you have seen how to use this in the previous section, example 2 in Babel-Standalone is the API used to simulate a script for live transcoding user input online This way, if you have seen how to use the cli command section you should remember the last question about the Q&A section why was the code output from the use example above not transcoded? There was no clear answer at that time, so the following two, using the CLI section describes how to use.

1. Use the CLI

introduce

The Babel CLI has been more or less covered in previous chapters, leaving the question of how to configure transcoding in Babel CLI. Let’s get straight to the point.

The sample

The project directory structure is as follows:

| - config - Babel - cli project name | -- package. JsonCopy the code
// package.json
{
  "name": "config-babel-cli"
}
Copy the code
  1. Use the command to enter the projectcd config-babel-cli
  2. Run partial installation commandsnpm install --save-dev babel-cli babel-plugin-transform-es2015-arrow-functions

After installation, the project directory structure is as follows:

| - config - Babel - cli project name | -- package. Json | -- package - lock. Json record installed version of the module, source information | - node_modules node set | - rely on module Babel - cli cli module | - Babel - plugin - transform - es2015 - arrow - functions provides the arrow conversion module | - Babel - * will begin with Babel is behind the various modules of the introductionCopy the code
// package.json
{
  "name": "config-babel-cli"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"}}Copy the code

Next we create a script file (script.js) in the project and transcode it.

// script.js
[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

Remember how to use the Babel CLI?

First, go to CD config-babel-cli, where the script resides, and then run NPX Babel script.js on the cli.

Note: You need to configure the command line herenpx babel --plugins babel-plugin-transform-es2015-arrow-functions script.js

Note: You need to configure the command line herenpx babel --plugins babel-plugin-transform-es2015-arrow-functions script.js

Note: You need to configure the command line herenpx babel --plugins babel-plugin-transform-es2015-arrow-functions script.js

Only in this way can we solve the problem that the code does not transfer after transcoding mentioned in the previous chapter.

There’s another NPM that corresponds to NPX remember? (If you don’t want to type so much on the command line… So use NPM), so you need to make changes to package.json (how to do this is described in the usage. Cli command section).

{
  "name": "config-babel-cli"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"
  },
  "scripts": {
    "babel": "babel --plugins babel-plugin-transform-es2015-arrow-functions script.js"."babel-compiled": "babel script.js --plugins babel-plugin-transform-es2015-arrow-functions --out-file script-compiled.js"}}Copy the code

After the scripts object is configured, NPM run Babel or NPM run babel-compiled is enough.

// script.js after transcoding
[1.2.3.4].forEach(function (item) {
  return console.log(item);
});
Copy the code
Question and answer

Is there another convenient way to configure it?

Either using THE API or using the CLI command line, there is a problem with the configuration is a bit cumbersome, the example just demonstrates the conversion arrow function,es6 so many new syntax, features do you need to configure N more on the command line… So the configuration described in the next section may solve this problem.

Three, babelrc

introduce

Let’s start with the origin of the suffix rc: in the UNIX world, rc is often used as the file name of a program’s startup script. It is short for “Run Commands”. The abbreviation is said to have originated from the 1965 MIT CTSS runcom order. — Wikipedia (bk.tw.lvfukeji.com/baike-Rc%E6…)

In Linux, the etc directory has. Bashrc. ZSHRC (the name of other versions may be bashrc ZSHRC).

Vue——.vuerc.

NPM – NPMRC.

[name]rc file, so the suffix rc file is not specific to a particular framework. Therefore, Babel can be configured with a file ending in rc, which will be read when Babel is called to start.

// node_modules/babel-core/lib/transformation/file/options/build-config-chain.js
var BABELRC_FILENAME = '.babelrc';
function findConfigs(loc) {
  var configLoc = _path2.default.join(loc, BABELRC_FILENAME);
}
Copy the code

FindConfigs (path/.babelrc)

With the suffix RC, then. babelrc prefix. ? It is used as a naming standard to hide files from view. So when the ls command is used to display the file list (list), it is not displayed. Prefix files.(Ls -a is required to display all files.)

Note :Window is created.babelrcFile, prompting you must type the file name, required when renaming.babelrc.Prefixes and suffixes.That’s it. That’s it.babelrcThe file.

So that’s a brief introduction to. Babelrc (just a quick reminder to check your hard drive to see if there are any files you’ve forgotten… Folder…). .

The sample
  • The sample a

The project directory structure is as follows:

| - config - Babel - babelrc project name. | - | babelrc configuration file -- package. Json | -- package - lock. Json record installation module | - node_modules version, source of information Node dependent module set | - Babel - cli cli module | - Babel - plugin - transform - es2015 - arrow - functions provides the arrow conversion module | - Babel - * will begin with Babel is behind the various modules of the introductionCopy the code
// .babelrc
{
  "plugins": ["babel-plugin-transform-es2015-arrow-functions"]}Copy the code
// package.json
{
  "name": "config-babel-babelrc"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"}}Copy the code
// script.js
[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

NPM run Babel returns the following result:

// script.js after transcoding
[1.2.3.4].forEach(function (item) {
  return console.log(item);
});
Copy the code
  • Example 2

Another configuration that corresponds to creating the configuration file. Babelrc is transcoding by adding a Babel object to package.json (in fact, moving configuration items from. Babelrc to packPage. json).

// package.json
{
  "name": "config-babel-babelrc"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"
  },
  "babel": {
    "plugins": ["babel-plugin-transform-es2015-arrow-functions"]}}Copy the code

Note: If there is.babelrcFile at the same timepackage.jsonConfiguration in thebabelObject is used preferentially.babelrcFile.

Note: If there is.babelrcFile at the same timepackage.jsonConfiguration in thebabelObject is used preferentially.babelrcFile.

Note: If there is.babelrcFile at the same timepackage.jsonConfiguration in thebabelObject is used preferentially.babelrcFile.

Question and answer

How does Babel find.babelrc files?

First, Babel uses a relative file configuration lookup, which looks up from the directory where the script is being executed. Babelrc files feel similar to how NPM looks up package.json. Here is the source code for the search. If you are interested, take a look at the while loop.

If multiple subdirectories exist.babelrcDocuments also adopt the principle of proximity.

// node_modules/babel-core/lib/transformation/file/options/build-config-chain.js

var BABELIGNORE_FILENAME = '.babelignore';
var BABELRC_FILENAME = '.babelrc';
var PACKAGE_FILENAME = 'package.json';

ConfigChainBuilder.prototype.findConfigs = function findConfigs(loc) {
  if(! loc)return;

  if(! (0, _pathIsAbsolute2.default)(loc)) {
    loc = _path2.default.join(process.cwd(), loc);
  }

  var foundConfig = false;
  var foundIgnore = false;

  while(loc ! == (loc = _path2.default.dirname(loc))) {if(! foundConfig) {var configLoc = _path2.default.join(loc, BABELRC_FILENAME);

      if (exists(configLoc)) {
        this.addConfig(configLoc);
        foundConfig = true;
      }

      var pkgLoc = _path2.default.join(loc, PACKAGE_FILENAME);
      if(! foundConfig && exists(pkgLoc)) { foundConfig =this.addConfig(pkgLoc, 'babel'.JSON); }}if(! foundIgnore) {var ignoreLoc = _path2.default.join(loc, BABELIGNORE_FILENAME);
      if (exists(ignoreLoc)) {
        this.addIgnoreConfig(ignoreLoc);
        foundIgnore = true; }}if (foundIgnore && foundConfig) return; }};Copy the code
supplement

Note the difference between NPM, NPX, and Babel on the output of the current working directory process. CWD.

The process.cwd() method returns the current working directory of the Node.js process according to the official explanation. Is there a difference in the working directory returned? With that in mind, let’s look at the following example to see if we can find the answer.

Let’s look at the project structure as follows:

| - config - Babel - babelrc project name. | - | babelrc configuration file -- package. Json | -- package - lock. Json record installation module | - node_modules version, source of information Node dependent module set | - Babel - cli cli module | - Babel - plugin - transform - es2015 - arrow - functions provides the arrow conversion module | - Babel - * will begin with Babel is behind the various modules of the introduction | - utils tools | script file collection -- index. Js | -- script. Js script fileCopy the code
// package.json
{
  "name": "config-babel-babelrc"."devDependencies": {
    "babel-cli": "^ 6.26.0"."babel-plugin-transform-es2015-arrow-functions": "^ 6.22.0"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"."utils": "babel ./utils/index.js"}}Copy the code

The following tests need to be prepared in advance:

  1. In the NPM package, find the /usr/local/lib/node_modules/ NPM /bin/npm-cli.js edit script insert output statement.

  2. In the NPM package (search for the installation location yourself), find the /usr/local/lib/node_modules/ NPM /bin/npx-cli.js edit script insert output statement.

  3. Find node_modules/ Babel -cli/bin/babel.js edit script insert output statement in node_modules package of the project.

// npm-cli.js
console.log('NPM output current working directory :', process.cwd());
Copy the code
// npx-cli.js
console.log('NPX outputs the current working directory :', process.cwd());
Copy the code
// babel.js
console.log('Babel outputs current working directory :', process.cwd());
Copy the code
  • Test 1: NPM and Babel output the current working directory in the root directory
  1. Command terminal to access the directorycd config-babel-babelrc
  2. Execute the commandnpm run babel
// Execute NPM run Babel to print the current working directoryNPM outputs current working directory:`/babel/examples/config-babel-babelrc`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code
  1. Execute the commandnpm run utils
// Run NPM run utils to print the current working directoryNPM outputs current working directory:`/babel/examples/config-babel-babelrc`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code

Conclusion: The current working directory in the root directory is the same whether the command NPM run Babel or NPM run utils is executed.

  • Test 2: NPM and Babel output the current working directory under utils
  1. Command terminal to access the directorycd config-babel-babelrc/utilsNote that the path here is to the utils directory
  2. Execute the commandnpm run babel
// Execute NPM run Babel to print the current working directoryNPM outputs current working directory:`/babel/examples/config-babel-babelrc/utils`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code
  1. Execute the commandnpm run utils
// Run NPM run utils to print the current working directoryNPM outputs current working directory:`/babel/examples/config-babel-babelrc/utils`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code

Conclusion: The current working directory is not the same at the end of NPM run Babel or NPM run utils in the utils directory.

  • Test 3: NPX and Babel output the current working directory in the root directory
  1. Command terminal to access the directorycd config-babel-babelrc
  2. Execute the commandnpx babel script.js
// Run NPX Babel script.js to output the current working directoryNPX outputs the current working directory:`/babel/examples/config-babel-babelrc`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code
  1. Execute the commandnpx babel utils/index.js
// Run NPX Babel utils/index.js to print the current working directoryNPX outputs the current working directory:`/babel/examples/config-babel-babelrc`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc`;
Copy the code

Conclusion: The current working directory is the same when NPX Babel script.js or NPX Babel utils/index.js is executed in the root directory.

  • Test 4: utils directory NPX and Babel output the current working directory
  1. Command terminal to access the directorycd config-babel-babelrc/utilsNote that the path here is to the utils directory
  2. Execute the commandnpx babel .. /script.js
// execute NPX Babel.. /script.js outputs the current working directoryNPX outputs the current working directory:`/babel/examples/config-babel-babelrc/utils`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc/utils`;
Copy the code
  1. Execute the commandnpx babel index.js
// Run NPX Babel index.js to output the current working directoryNPX outputs the current working directory:`/babel/examples/config-babel-babelrc/utils`; Babel outputs the current working directory:`/babel/examples/config-babel-babelrc/utils`;
Copy the code

Conclusion: In the utils directory regardless of the execution of the command NPX Babel.. /script.js or NPX Babel index.js will output the same current working directory.

From the above four tests, only test 2 results are different from the other tests, that is, when running NPM run in the utils directory… NPM output is inconsistent with Babel output. So let’s start with the analysis.

First let’s take a look at what NPM does roughly.

Run NPM run… NPM /bin/npm-cli.js-> NPM /lib/npm.js

// npm/lib/npm.js
Object.keys(abbrevs)
  .concat(plumbing)
  .forEach(function addCommand(c) {
    Object.defineProperty(npm.commands, c, {
      get: function () {
        if(! loaded) {throw new Error(
            'Call npm.load(config, cb) before using this command.\n' +
              'See the README.md or bin/npm-cli.js for example usage.'
          );
        }
        var a = npm.deref(c);
        if (c === 'la' || c === 'll') {
          npm.config.set('long'.true);
        }

        npm.command = c;
        if (commandCache[a]) return commandCache[a];

        var cmd = require(path.join(__dirname, a + '.js'));

        commandCache[a] = function () {
          var args = Array.prototype.slice.call(arguments.0);
          if (typeof args[args.length - 1]! = ='function') {
            args.push(defaultCb);
          }
          if (args.length === 1) args.unshift([]);

          // Options are prefixed by a hyphen-minus (-, \u2d).
          // Other dash-type chars look similar but are invalid.
          Array(args[0]).forEach(function (arg) {
            if (/^[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/.test(arg)) {
              log.error(
                'arg'.'Argument starts with non-ascii dash, this is probably invalid:', arg ); }});if(! registryRefer) { registryRefer = [a] .concat(args[0])
              .map(function (arg) {
                // exclude anything that might be a URL, path, or private module
                // Those things will always have a slash in them somewhere
                if (arg && arg.match && arg.match(/ \ | \ \ /)) {
                  return '[REDACTED]';
                } else {
                  return arg;
                }
              })
              .filter(function (arg) {
                return arg && arg.match;
              })
              .join(' ');
            npm.referer = registryRefer;
          }

          cmd.apply(npm, args);
        };

        Object.keys(cmd).forEach(function (k) {
          commandCache[a][k] = cmd[k];
        });

        return commandCache[a];
      },
      enumerable: fullList.indexOf(c) ! = = -1.configurable: true});// make css-case commands callable via camelCase as well
    if (c.match(/-([a-z])/)) {
      addCommand(
        c.replace(/-([a-z])/g.function (a, b) {
          returnb.toUpperCase(); })); }});Copy the code

Add the corresponding execution script to the cmdList(NPM /lib/config/cmd-list.js) command list (for example, the run command corresponds to NPM /lib/run-script.js), and then run cmd.apply(NPM, Args), runScript is called

// npm/lib/run-script.js
function runScript(args, cb) {
  if(! args.length)return list(cb);

  var pkgdir = npm.localPrefix;
  var cmd = args.shift();

  readJson(path.resolve(pkgdir, 'package.json'), function (er, d) {
    if (er) return cb(er);
    run(d, pkgdir, cmd, args, cb);
  });
}
function run(pkg, wd, cmd, args, cb) {
  if(! pkg.scripts) pkg.scripts = {};var cmds;
  if (cmd === 'restart' && !pkg.scripts.restart) {
    cmds = [
      'prestop'.'stop'.'poststop'.'restart'.'prestart'.'start'.'poststart',]; }else {
    if (pkg.scripts[cmd] == null) {
      if (cmd === 'test') {
        pkg.scripts.test = "echo 'Error: no test specified'";
      } else if (cmd === 'env') {
        if (isWindowsShell) {
          log.verbose('run-script using default platform env: SET (Windows)');
          pkg.scripts[cmd] = 'SET';
        } else {
          log.verbose('run-script using default platform env: env (Unix)');
          pkg.scripts[cmd] = 'env'; }}else if (npm.config.get('if-present')) {
        return cb(null);
      } else {
        let suggestions = didYouMean(cmd, Object.keys(pkg.scripts));
        suggestions = suggestions ? '\n' + suggestions : ' ';
        return cb(new Error('missing script: ' + cmd + suggestions));
      }
    }
    cmds = [cmd];
  }

  if(! cmd.match(/^(pre|post)/)) {
    cmds = ['pre' + cmd].concat(cmds).concat('post' + cmd);
  }

  log.verbose('run-script', cmds);
  chain(
    cmds.map(function (c) {
      // pass cli arguments after -- to script.
      if (pkg.scripts[c] && c === cmd) {
        pkg.scripts[c] = pkg.scripts[c] + joinArgs(args);
      }

      // when running scripts explicitly, assume that they're trusted.
      return [lifecycle, pkg, c, wd, { unsafePerm: true }];
    }),
    cb
  );
}
Copy the code

Attention! Attention! Attention! The key is that the runScript function defines the pkgdir variable and loads the package.json file with pkgdir as the path readJson. pkgdir, cmd, args, cb); Function to pass this variable as an argument. So the following code gets the working directory as the pkgdir variable.

Now you know why test two occurs (conclusion: the current working directory is not the same at the end of the command NPM run Babel or NPM run utils in the utils directory). , because NPM finds the package.json path and passes it as wd. So when Babel is called later, console.log(‘ Babel path :’,process.cwd()) is output; Is it.

The following is part of the subsequent execution code

// npm/lib/run-script.js
chain(
  cmds.map(function (c) {
    // pass cli arguments after -- to script.
    if (pkg.scripts[c] && c === cmd) {
      pkg.scripts[c] = pkg.scripts[c] + joinArgs(args);
    }

    // when running scripts explicitly, assume that they're trusted.
    return [lifecycle, pkg, c, wd, { unsafePerm: true }];
  }),
  cb
);
Copy the code
// npm/node_modules/slide/lib/chain.js
module.exports = chain;
var bindActor = require('./bind-actor.js');
chain.first = {};
chain.last = {};
function chain(things, cb) {
  var res = [];
  (function LOOP(i, len) {
    if (i >= len) return cb(null, res);
    if (Array.isArray(things[i]))
      things[i] = bindActor.apply(
        null,
        things[i].map(function (i) {
          return i === chain.first
            ? res[0]
            : i === chain.last
            ? res[res.length - 1] : i; }));if(! things[i])return LOOP(i + 1, len);
    things[i](function (er, data) {
      if (er) return cb(er, res);
      if(data ! = =undefined) res = res.concat(data);
      LOOP(i + 1, len); }); }) (0, things.length);
}
Copy the code
// npm/node_modules/npm-lifecycle/index.js
function lifecycle(pkg, stage, wd, opts) {
  lifecycle_(pkg, stage, wd, opts, env, (er) = > {
    if (er) return reject(er);
    return resolve();
  });
}

function lifecycle_(pkg, stage, wd, opts, env, cb) {
  chain(
    [
      packageLifecycle && [runPackageLifecycle, pkg, stage, env, wd, opts],
      [runHookLifecycle, pkg, stage, env, wd, opts],
    ],
    done
  );
}
function runPackageLifecycle(pkg, stage, env, wd, opts, cb) {
  // run package lifecycle scripts in the package root, or the nearest parent.
  var cmd = env.npm_lifecycle_script;

  var note = '\n> ' + pkg._id + ' ' + stage + ' ' + wd + '\n> ' + cmd + '\n';
  runCmd(note, cmd, pkg, env, stage, wd, opts, cb);
}
function runCmd(note, cmd, pkg, env, stage, wd, opts, cb) {
  if (unsafe) {
    runCmd_(cmd, pkg, env, wd, opts, stage, unsafe, 0.0, cb);
  } else {
    uidNumber(user, group, function (er, uid, gid) {
      if (er) {
        er.code = 'EUIDLOOKUP';
        opts.log.resume();
        process.nextTick(dequeue);
        returncb(er); } runCmd_(cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb); }); }}function runCmd_(cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
  function cb(er) {
    cb_.apply(null.arguments);
    opts.log.resume();
    process.nextTick(dequeue);
  }

  const [sh, args, conf] = getSpawnArgs({
    cmd,
    wd,
    opts,
    uid,
    gid,
    unsafe,
    env,
  });

  opts.log.verbose('lifecycle', logid(pkg, stage), 'PATH:', env[PATH]);
  opts.log.verbose('lifecycle', logid(pkg, stage), 'CWD:', wd);
  opts.log.silly('lifecycle', logid(pkg, stage), 'Args:', args);

  var proc = spawn(sh, args, conf, opts.log);
}
Copy the code
// npm/node_modules/npm-lifecycle/lib/spawn.js
const _spawn = require('child_process').spawn;
function spawn(cmd, args, options, log) {
  const cmdWillOutput = willCmdOutput(options && options.stdio);

  if (cmdWillOutput) startRunning(log);
  const raw = _spawn(cmd, args, options);
  const cooked = new EventEmitter();
}
Copy the code
/ / https://github.com/nodejs/node/blob/v14.16.1/lib/child_process.js
function spawn(file, args, options) {
  const child = new ChildProcess();

  options = normalizeSpawnArguments(file, args, options);
  debug('spawn', options);
  child.spawn(options);

  return child;
}

function normalizeSpawnArguments(file, args, options) {
  return {
    // Make a shallow copy so we don't clobber the user's options object.. options, args,detached:!!!!! options.detached, envPairs, file,windowsHide:!!!!! options.windowsHide,windowsVerbatimArguments:!!!!! windowsVerbatimArguments, }; }Copy the code

The reason for showing so much of the following code is simply to show that:

  1. The wd argument is passed all the time.
  2. Finally, inspawn.jsIn the script_spawn(cmd, args, options)Parameter when creating a child processoptionsKey in (object type)cwdThe corresponding value is the parameterwd.
  3. this_spawnFunction new child process is nextbabel. (babelThe receivedprocess.cwdIt comes from abovenpm).

Four, Babel. Config. Js

introduce

This configuration can be applied to the entire project by creating a new babel.config.js configuration file in the root directory of the project. It is placed last because it is new to Babel7.

Can refer to the website of Babel. Config. Js more introduce www.babeljs.cn/docs/config… .

The sample

The project structure is as follows:

| - config - Babel - babelconfig project name | -- Babel. Config. | js configuration file -- package. Json | -- package - lock. Json record installation module | - version, source of information Node_modules node dependent module set | - Babel - cli cli module core module | | - Babel - core - the Babel - plugin - transform - es2015 - arrow - functions provides Arrow conversion module | - Babel - * begin with Babel behind each module will introduce | -- script. Js script fileCopy the code
// package.json
{
  "name": "config-babel-babelconfig"."devDependencies": {
    "@babel/cli": "^ 7.12.10"."@babel/core": "^ 7.12.10"."@babel/plugin-transform-arrow-functions": "^ 7.12.1"
  },
  "scripts": {
    "babel": "babel script.js"."babel-compiled": "babel script.js --out-file script-compiled.js"}}Copy the code
// babel.config.js
module.exports = function (api) {
  api.cache(true);

  const plugins = ['@babel/plugin-transform-arrow-functions'];

  return {
    plugins,
  };
};
Copy the code
// script.js
[1.2.3.4].forEach((item) = > console.log(item));
Copy the code

NPM run Babel

// script.js after transcoding
[1.2.3.4].forEach(function (item) {
  return console.log(item);
});
Copy the code

The example is relatively simple and demonstrates the new root-based configuration of Babel7.

supplement

This addendum could be a lot wordy… So be sure to slap yourself to stay awake (two slaps work faster).

Project root configuration mode.

This is a new feature in Babel7 to retrieve a babel.config.js file or.json,.cjs,.mjs file in the root directory, which defaults to the current directory, since babel7 provides configuration files with multiple suffixes.

// node_modules/@babel/core/lib/config/files/configuration.js
const ROOT_CONFIG_FILENAMES = [
  'babel.config.js'.'babel.config.cjs'.'babel.config.mjs'.'babel.config.json',];Copy the code

Above is the file name that the source code defines to retrieve. This can also be seen by the variable name (root configuration file name).

Note that it is retrieved only from the root directory. You can also specify that the configuration file is loaded by configuring configFile

How does babel7’s new root-based configuration differ from the previous relative directory configuration?

In fact, the official website link provided above has already answered this question, here is just to understand.

Babelrc is a relative configuration (you can write configuration files in different directories).