Rollup basic use and handwriting to achieve treeshaking function

Jiushang 2021-05-04 18:52:44
rollup basic use handwriting achieve


rollup It's the next generation ES Module binder

1.1 background

  • webpack Packing is very cumbersome , The packing volume is relatively large
  • rollup It's mainly for packing JS Library
  • vue/react/angular All in use rollup As a packaging tool

1.2 Installation dependency

cnpm i @babel/core @babel/preset-env @rollup/plugin-commonjs @rollup/plugin-node-resolve @rollup/plugin-typescript lodash rollup rollup-plugin-babel postcss rollup-plugin-postcss rollup-plugin-terser tslib typescript rollup-plugin-serve rollup-plugin-livereload -D
 Copy code 

1.3 First use

1.3.1 rollup.config.js

  • Asynchronous Module Definition Asynchronous module definition
  • ES6 module yes es6 A new modularization scheme is proposed
  • IIFE(Immediately Invoked Function Expression) That is, the function expression is executed immediately , Immediate execution , Is to declare a function , When the statement is finished, execute it immediately
  • UMD Its full name is Universal Module Definition, That's the general module definition
  • cjs yes nodejs Modular standards adopted ,commonjs Usage method require To introduce modules , here require() The received parameter is the module name or the path of the module file

rollup.config.js

export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'cjs',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName'// When format by iife and umd Must provide , Hang as a global variable in window Next 
}
}
 Copy code 

1.3.2 src\main.js

console.log('hello');
 Copy code 

1.3.3 package.json

{
"scripts": {
"build": "rollup --config"
},
}
 Copy code 

1.3.4 dist\index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rollup</title>
</head>
<body>
<script src="bundle.cjs.js"></script>
</body>
</html>
 Copy code 

1.4 Support babel

In order to use the new syntax , have access to babel To compile the output

1.4.1 Installation dependency

  • @babel/core yes babel The core of the package
  • @babel/preset-env It's presupposition
  • rollup-plugin-babel yes babel plug-in unit
cnpm install rollup-plugin-babel @babel/core @babel/preset-env --save-dev
 Copy code 

1.4.2 src\main.js

let sum = (a,b)=>{
return a+b;
}
let result = sum(1,2);
console.log(result);
 Copy code 

1.4.3 .babelrc

{
"presets": [
[
"@babel/env",
{
"modules":false
}
]
]
}
 Copy code 

1.4.4 rollup.config.js

+import babel from 'rollup-plugin-babel';
export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'cjs',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName'// When format by iife and umd Must provide , Hang as a global variable in window Next 
},
+ plugins:[
+ babel({
+ exclude:"node_modules/**"
+ })
+ ]
}
 Copy code 

1.5 tree-shaking

  • Tree-shaking The essence of is to eliminate useless js Code
  • rollup Only functions and top level import/export Variable

1.5.1 src\main.js

src\main.js

import {name,age} from './msg';
console.log(name);
 Copy code 

1.5.2 src\msg.js

export var name = 'beijing';
export var age = 12;
 Copy code 

1.6 Use third-party modules

rollup.js By default, module references in compiled source code only support ES6+ The modular approach import/export

1.6.1 Installation dependency

cnpm install @rollup/plugin-node-resolve @rollup/plugin-commonjs lodash --save-dev
 Copy code 

1.6.2 src\main.js

import _ from 'lodash';
console.log(_);
 Copy code 

1.6.3 rollup.config.js

import babel from 'rollup-plugin-babel';
+import resolve from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'cjs',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName'// When format by iife and umd Must provide , Hang as a global variable in window Next 
},
plugins:[
babel({
exclude:"node_modules/**"
}),
+ resolve(),
+ commonjs()
]
}
 Copy code 

1.7 Use CDN

1.7.1 src\main.js

import _ from 'lodash';
import $ from 'jquery';
console.log(_.concat([1,2,3],4,5));
console.log($);
export default 'main';
 Copy code 

1.7.2 dist\index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rollup</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery/jquery.min.js"></script>
<script src="bundle.cjs.js"></script>
</body>
</html>
 Copy code 

1.7.3 rollup.config.js

import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
+ format:'iife',// Five output formats :amd/es6/iife/umd/cjs
+ name:'bundleName',// When format by iife and umd Must provide , Hang as a global variable in window Next 
+ globals:{
+ lodash:'_', // tell rollup Global variables _ That is lodash
+ jquery:'$' // tell rollup Global variables $ That is jquery
+ }
},
plugins:[
babel({
exclude:"node_modules/**"
}),
resolve(),
commonjs()
],
+ external:['lodash','jquery']
}
 Copy code 

1.8 Use typescript

1.8.1 install

cnpm install tslib typescript @rollup/plugin-typescript --save-dev
 Copy code 

1.8.2 src\main.ts

let myName:string = 'beijing';
let age:number=12;
console.log(myName,age);
 Copy code 

1.9 Compress JS

terser It's supporting ES6 + Of JavaScript Compressor kit

1.9.1 install

cnpm install rollup-plugin-terser --save-dev
 Copy code 

1.9.2 rollup.config.js

import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
+import {terser} from 'rollup-plugin-terser';
export default {
input:'src/main.ts',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'iife',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName',// When format by iife and umd Must provide , Hang as a global variable in window Next 
globals:{
lodash:'_', // tell rollup Global variables _ That is lodash
jquery:'$' // tell rollup Global variables $ That is jquery
}
},
plugins:[
babel({
exclude:"node_modules/**"
}),
resolve(),
commonjs(),
typescript(),
+ terser(),
],
external:['lodash','jquery']
}
 Copy code 

1.10 compile css

rollup-plugin-postcs Plug ins support compilation css

1.10.1 install

cnpm install postcss rollup-plugin-postcss --save-dev
 Copy code 

1.10.2 rollup.config.js

import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import {terser} from 'rollup-plugin-terser';
+import postcss from 'rollup-plugin-postcss';
export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'iife',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName',// When format by iife and umd Must provide , Hang as a global variable in window Next 
globals:{
lodash:'_', // tell rollup Global variables _ That is lodash
jquery:'$' // tell rollup Global variables $ That is jquery
}
},
plugins:[
babel({
exclude:"node_modules/**"
}),
resolve(),
commonjs(),
typescript(),
//terser(),
+ postcss()
],
external:['lodash','jquery']
}
 Copy code 

1.10.3 src\main.js

import './main.css';
 Copy code 

1.10.4 src\main.css

body{
background-color: green;
}
 Copy code 

1.11 Local server

1.11.1 install

cnpm install rollup-plugin-serve --save-dev
 Copy code 

1.11.2 rollup.config.dev.js

import babel from 'rollup-plugin-babel';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
+import serve from 'rollup-plugin-serve';
export default {
input:'src/main.js',
output:{
file:'dist/bundle.cjs.js',// The path and name of the output file 
format:'iife',// Five output formats :amd/es6/iife/umd/cjs
name:'bundleName',// When format by iife and umd Must provide , Hang as a global variable in window Next 
sourcemap:true,
globals:{
lodash:'_', // tell rollup Global variables _ That is lodash
jquery:'$' // tell rollup Global variables $ That is jquery
}
},
plugins:[
babel({
exclude:"node_modules/**"
}),
resolve(),
commonjs(),
typescript(),
postcss(),
+ serve({
+ open:true,
+ port:8080,
+ contentBase:'./dist'
+ })
],
external:['lodash','jquery']
}
 Copy code 

1.11.3 package.json

{
"scripts": {
"build": "rollup --config rollup.config.build.js",
"dev": "rollup --config rollup.config.dev.js -w"
},
}
 Copy code 

2. Pre knowledge

2.1. Initialize project

cnpm install magic-string acorn --save
 Copy code 

2.2. magic-string

magic-string Is an operation string and generate source-map Tools for doc/1.js

var MagicString = require('magic-string');
var magicString = new MagicString('export var name = "beijing"');
// Similar to intercepting strings 
console.log(magicString.snip(0,6).toString()); // export
// Delete string from start to finish ( The index is always based on the original string , Not after the change )
console.log(magicString.remove(0,7).toString()); // var name = "beijing"
// Many modules , Package them in one file , You need to merge the source code of many files together 
let bundleString = new MagicString.Bundle();
bundleString.addSource({
content:'var a = 1;',
separator:'\n'
});
bundleString.addSource({
content:'var b = 2;',
separator:'\n'
});
/* let str = '';
str += 'var a = 1;\n'
str += 'var b = 2;\n'
console.log(str); */
console.log(bundleString.toString());
// var a = 1;
//var b = 2;
 Copy code 

2.3. AST

adopt JavaScript Parser You can turn code into an abstract syntax tree AST, This tree defines the structure of the code , By manipulating this tree , We can precisely locate the statement 、 Assignment statement 、 Operational statements and so on , Implementation of code analysis 、 Optimize 、 Change, etc

2.3.1 AST workflow

  • Parse( analysis ) Convert the source code into an abstract syntax tree , There are a lot of estree node
  • Transform( transformation ) Transform the abstract syntax tree
  • Generate( Code generation ) Generate new code from the converted abstract syntax tree in the previous step

2.3.2 acorn

  • astexplorer You can turn the code into a syntax tree

  • acorn The analytical results are consistent with The Estree Spec standard

import $ from 'jquery Of ast Here's the picture

2.3.2.1 walk.js

doc/walk.js

/**
*
* @param {*} ast The syntax tree to traverse
* @param {*} param1 Configuration object
*/
function walk(ast, { enter, leave }) {
visit(ast, null, enter, leave);
}
/**
* Visit this node node
* @param {*} node
* @param {*} parent
* @param {*} enter
* @param {*} leave
*/
function visit(node, parent, enter, leave) {
if (enter) {// First perform the enter Method 
enter(node, parent);// No concern this That's how it's written 
//enter.call(null,node,parent);// If you want to specify enter Medium this
}
// Then traverse the child nodes Find out which are the child nodes of the object 
let childKeys = Object.keys(node).filter(key => typeof node[key] === 'object');
childKeys.forEach(childKey => {//childKey=specifiers value=[]
let value = node[childKey];
if (Array.isArray(value)) {
value.forEach((val) => visit(val, node, enter, leave));
} else {
visit(value, node, enter, leave)
}
});
// Then execute the exit method 
if (leave) {
leave(node, parent);
}
}
module.exports = walk;
 Copy code 

2.3.2.2 ast.js

doc/useWZalk.js

// stay webpack Li He rollup Is to use acorn The module turns the source code into an abstract syntax tree AST
let acorn = require('acorn');
let walk = require('./walk');
//parse Method to turn the source code into an abstract syntax tree 
let astTree = acorn.parse(`import $ from 'jquery';`,{
locations:true,ranges:true,sourceType:'module',ecmaVersion:8
});
let ident = 0;
const padding = ()=>" ".repeat(ident);
//console.log(astTree.body);
// Traverse every statement in the syntax tree 
astTree.body.forEach(statement=>{
// Each statement is passed to walk Method , from walk Traverse this sentence element 
// We use the depth first method to traverse 
walk(statement,{
enter(node){
if(node.type){
console.log(padding()+node.type+' Get into ');
ident+=2;
}
},
leave(node){
if(node.type){
ident-=2;
console.log(padding()+node.type+' Leave ');
}
}
});
});
 Copy code 

The depth traversal is shown in the figure Print the results

ImportDeclaration Get into
ImportDefaultSpecifier Get into
Identifier Get into
Identifier Leave
ImportDefaultSpecifier Leave
Literal Get into
Literal Leave
ImportDeclaration Leave
 Copy code 

2.4 Scope

Scope chain is composed of a series of variable objects of current execution environment and upper execution environment , It ensures that the current execution environment has orderly access to variables and functions that meet the access rights doc/scope.js

class Scope{
constructor(options = {}){
this.name = options.name;// The scope has a name , It's no use , Just to help You know 
this.parent = options.parent;// Parent scope 
this.names = options.params||[];// What are the variables in this action 
}
add(name){
this.names.push(name);
}
findDefiningScope(name){
if(this.names.includes(name)){
return this;
}
if(this.parent){
return this.parent.findDefiningScope(name);
}
return null;
}
}
module.exports = Scope;
 Copy code 

doc/usescope.js

let Scope = require('./scope');
let a = 1;
function one(){
let b = 2;
function two(age){
let c = 3;
console.log(a,b,c,age);
}
two();
}
one();
let globalScope = new Scope({
name:'globalScope',params:[],parent:null
});
globalScope.add('a');
let oneScope = new Scope({
name:'oneScope',params:[],parent:globalScope
});
oneScope.add('b');
let twoScope = new Scope({
name:'twoScope',params:['age'],parent:oneScope
});
twoScope.add('c');
let aScope =twoScope.findDefiningScope('a');
console.log(aScope.name); //globalScope
let bScope =twoScope.findDefiningScope('b');
console.log(bScope.name);//oneScope
let cScope =twoScope.findDefiningScope('c');
console.log(cScope.name);//twoScope
let ageScope =twoScope.findDefiningScope('age');
console.log(ageScope.name);//twoScope
let xxxScope =twoScope.findDefiningScope('xxx');
console.log(xxxScope);//null
//tree-shaking The core of the principle is based on such a theory scope chain
 Copy code 

3. Realization rollup

rollup Warehouse address

3.1 src\msg.js

export var name = 'xiaoming';
export var age = 12;
 Copy code 

3.2 src\main.js

import {name,age} from './msg';
function say(){
console.log('hello',name);
}
say();
 Copy code 

Packing results

var name = 'xiaoming';
function say(){
console.log('hello',name);
}
say();
 Copy code 

3.3 debugger.js

const path = require('path');
const rollup = require('./lib/rollup');
// The absolute path of the entry file 
let entry = path.resolve(__dirname,'src/main.js');
rollup(entry,'bundle.js');
 Copy code 

3.4 lib/rollup.js

let Bundle = require('./bundle');
function rollup(entry,outputFileName){
//Bundle It means packing objects , It will contain all the module information 
const bundle = new Bundle({entry});
// call build Method starts compiling 
bundle.build(outputFileName);
}
module.exports = rollup;
 Copy code 

3.5 lib/bundle.js

const fs = require('fs');
const path = require('path');
const { default: MagicString } = require('magic-string');
const Module = require('./module');
class Bundle{
constructor(options){
// The absolute path of the entry file , Include suffix 
this.entryPath = options.entry.replace(/\.js$/,'')+'.js';
this.modules = {};// It holds all the modules The entry file and the modules it depends on 
}
build(outputFileName){
// From the absolute path of the entry file, find its module definition 
let entryModule = this.fetchModule(this.entryPath);
// Expand all the statements of this entry module , Returns an array of all statements 
this.statements = entryModule.expandAllStatements();
const {code} = this.generate();
fs.writeFileSync(outputFileName,code,'utf8');
}
// Get module information 
fetchModule(importee,importer){
let route;
if(!importer){// If there is no module to import this module , This is the entry module 
route=importee;
}else{
if(path.isAbsolute(importee)){// If it's an absolute path 
route=importee;
}else if(importee[0]=='.'){// If the relative path 
route=path.resolve(path.dirname(importer),importee.replace(/\.js$/,'')+'.js');
}
}
if(route){
// Read the source code of this module from the hard disk 
let code = fs.readFileSync(route,'utf8');
let module = new Module({
code,// The source code of the module 
path:route,// The absolute path of the module 
bundle:this// Which is it? Bundle
});
return module;
}
}
// hold this.statements The generated code 
generate(){
let magicString = new MagicString.Bundle();
this.statements.forEach(statement=>{
const source = statement._source;
if(statement.type === 'ExportNamedDeclaration' && statement.declaration.type === 'VariableDeclaration'){
source.remove(statement.start,statement.declaration.start);
}
// if (/export/.test(statement.type)) {
// if (statement.type === 'ExportNamedDeclaration' && statement.declaration.type === 'VariableDeclaration') {
// source.remove(statement.start, statement.declaration.start);
// }
// }
magicString.addSource({
content:source,
separator:'\n'
});
});
return {code:magicString.toString()};
}
}
module.exports = Bundle;
 Copy code 

3.6 lib/module.js

let MagicString = require('magic-string');
const {parse} = require('acorn');
const analyse = require('./ast/analyse');
// Judge obj Whether there is prop attribute 
function hasOwnProperty(obj,prop){
return Object.prototype.hasOwnProperty.call(obj,prop);
}
/**
* Each file is a module , There will be one for each module Module example
*/
class Module{
constructor({code,path,bundle}){
this.code = new MagicString(code,{filename:path});
this.path = path;// The path of the module 
this.bundle = bundle;// Which is it? bundle Example 
this.ast = parse(code,{// Turn the source code into an abstract syntax tree 
ecmaVersion:7,
sourceType:'module'
});
this.analyse();
}
analyse(){
this.imports = {};// It stores all the imports of the current module 
this.exports = {};// It stores all the exports of the current module 
this.ast.body.forEach(node=>{
if(node.type === 'ImportDeclaration'){// Explain that this is an import statement 
let source = node.source.value;//./msg Import from which module 
let specifiers = node.specifiers;
specifiers.forEach(specifier=>{
const name = specifier.imported.name;//name
const localName = specifier.local.name;//name
// Which local variable , From which variable of which module 
//this.imports.age = {name:'age',localName:"age",source:'./msg'};
this.imports[localName]={name,localName,source}
});
//}else if(/^Export/.test(node.type)){
}else if(node.type === 'ExportNamedDeclaration'){
let declaration = node.declaration;//VariableDeclaration
if(declaration.type === 'VariableDeclaration'){
let name = declaration.declarations[0].id.name;//age
// Record the export of the current module This age By which expression 
//this.exports['age']={node,localName:age,expression}
this.exports[name] = {
node,localName:name,expression:declaration
}
}
}
});
analyse(this.ast,this.code,this);// eureka _defines and _dependsOn
this.definitions = {};// The definition statement that stores all global variables 
this.ast.body.forEach(statement=>{
Object.keys(statement._defines).forEach(name=>{
//key Is the global variable name , Value is the statement that defines the global variable 
this.definitions[name]=statement;
});
});
}
// Expand the statements in this module , Put the statements of variables defined in some statements into the result 
expandAllStatements(){
let allStatements = [];
this.ast.body.forEach(statement=>{
if(statement.type === 'ImportDeclaration'){return}
let statements = this.expandStatement(statement);
allStatements.push(...statements);
});
return allStatements;
}
// Expand a node 
// Find the variable that the current node depends on , The variables it accesses , Find the declaration statements for these variables .
// These statements may be declared in the current module , It may also be in the declaration of the imported module 
expandStatement(statement){
let result = [];
const dependencies = Object.keys(statement._dependsOn);// External dependence [name]
dependencies.forEach(name=>{
// Find the declaration node that defines the variable , This node can be in the current module , It could also be in a dependent module 
let definition = this.define(name);
result.push(...definition);
});
if(!statement._included){
statement._included = true;// Indicates that the node has been determined to be included in the result In the , You don't need to add it again in the future 
result.push(statement);
}
return result;
}
define(name){
// Find out if the import variable has name
if(hasOwnProperty(this.imports,name)){
//this.imports.age = {name:'age',localName:"age",source:'./msg'};
const importData = this.imports[name];
// obtain msg modular exports imports msg modular 
const module = this.bundle.fetchModule(importData.source,this.path);
//this.exports['age']={node,localName:age,expression}
const exportData = module.exports[importData.name];
// call msg Modular define Method , Parameter is msg The local variable name of the module age, The purpose is to return to the definition age Variable statement 
return module.define(exportData.localName);
}else{
//definitions It's the object ,key The variable name of the current module , The value is the statement that defines the variable 
let statement = this.definitions[name];
if(statement && !statement._included){
return this.expandStatement(statement);
}else{
return [];
}
}
}
}
module.exports = Module;
 Copy code 

3.7 analyse.js

lib\ast\analyse.js

let Scope = require('./scope');
let walk = require('./walk');
/**
* Find out which variables are used by the current module
* Also know which variables are declared by the current module , Which variables are imported into other modules
* @param {*} ast Grammar tree
* @param {*} magicString Source code
* @param {*} module Which module does it belong to
*/
function analyse(ast,magicString,module){
let scope = new Scope();// First, create a global scope within the module 
// Traverse all the top nodes of the current syntax tree 
ast.body.forEach(statement=>{
// Add variables to the scope var function const let Variable declarations 
function addToScope(declaration){
var name = declaration.id.name;// Get the declared variable 
scope.add(name);// hold say This variable is added to the current global scope 
if(!scope.parent){// If it is currently global scope 
statement._defines[name]=true;// Declare a global variable in the global scope say
}
}
Object.defineProperties(statement,{
_defines:{value:{}},// Store all global variables defined by the current module 
_dependsOn:{value:{}},// Variables that are not defined but used by the current module , That is, dependent external variables 
_included:{value:false,writable:true},// Whether this statement has Included in the packaging results 
//start Refers to the starting index of this node in the source code ,end It's the end of the index 
//magicString.snip Back again magicString example clone
_source:{value:magicString.snip(statement.start,statement.end)}
});
// This step is to build our scope chain 
+ // Collect each statement Variables defined on , Create a scope chain 
walk(statement,{
enter(node){
let newScope;
switch(node.type){
case 'FunctionDeclaration':
const params = node.params.map(x=>x.name);
if(node.type === 'FunctionDeclaration'){
addToScope(node);
}
// If you traverse to a function declaration , I'll create a new scope object 
newScope = new Scope({
parent:scope,// The parent scope is the current scope 
params
});
break;
case 'VariableDeclaration': // It doesn't generate a new scope 
node.declarations.forEach(addToScope);
break;
}
if(newScope){// The current node declares a new scope 
// If this node generates a new scope , Then we will put a _scope, Point to a new scope 
Object.defineProperty(node,'_scope',{value:newScope});
scope = newScope;
}
},
leave(node){
if(node._scope){// If this node produces a new scope , When you leave this node ,scope Back to the parent scope 
scope = scope.parent;
}
}
});
});
console.log(' First traversal ',scope);
ast._scope = scope;
// Identify external dependencies _dependsOn
ast.body.forEach(statement=>{
walk(statement,{
enter(node){
if(node._scope ){
scope = node._scope;
} // If this node has one scope A friend of mine , It means that this node creates a new scope 
if(node.type === 'Identifier'){
// Recursion up from the current scope , Find out in which scope the variable is defined 
const definingScope = scope.findDefiningScope(node.name);
if(!definingScope){
statement._dependsOn[node.name]=true;// Indicates that this is an externally dependent variable 
}
}
},
leave(node){
if(node._scope) {
scope = scope.parent;
}
}
});
});
}
module.exports = analyse;
 Copy code 

3.7 scope.js

lib\ast\scope.js

class Scope{
constructor(options = {}){
this.name = options.name;// The scope has a name , It's no use , Just to help You know 
this.parent = options.parent;// Parent scope 
this.names = options.params||[];// What are the variables in this action 
}
add(name){
this.names.push(name);
}
findDefiningScope(name){
if(this.names.includes(name)){
return this;
}
if(this.parent){
return this.parent.findDefiningScope(name);
}
return null;
}
}
module.exports = Scope;
 Copy code 

3.8 walk.js

lib\ast\walk.js

function walk(ast, { enter, leave }) {
visit(ast, null, enter, leave);
}
/**
* Visit this node node
* @param {*} node Traversal nodes
* @param {*} parent Parent node
* @param {*} enter How to enter
* @param {*} leave The way to leave
*/
function visit(node, parent, enter, leave) {
if (enter) {// First perform the enter Method 
enter(node, parent);// No concern this That's how it's written 
//enter.call(null,node,parent);// If you want to specify enter Medium this
}
// Then traverse the child nodes Find out which are the child nodes of the object 
let childKeys = Object.keys(node).filter(key => typeof node[key] === 'object');
childKeys.forEach(childKey => {//childKey=specifiers value=[]
let value = node[childKey];
if (Array.isArray(value)) {
value.forEach((val) => visit(val, node, enter, leave));
} else if(value && value.type) {// When traversing, only type The object node of the property must exist , And it's a place with type Object nodes for 
visit(value, node, enter, leave)
}
});
// Then execute the exit method 
if (leave) {
leave(node, parent);
}
}
module.exports = walk;
 Copy code 

Warehouse address

版权声明
本文为[Jiushang]所创,转载请带上原文链接,感谢
https://qdmana.com/2021/05/20210504184939011M.html

  1. Gallop workflow engine design series 01 process element design
  2. VUE移动端音乐APP学习【十六】:播放器歌词显示开发
  3. Vue Mobile Music App learning [16]: player lyrics display development
  4. jquery cookie
  5. jquery cookie
  6. 体面编码之JavaScript
  7. JavaScript for decent coding
  8. React17 系统精讲 结合TS打造旅游电商平台
  9. React17 system combined with TS to build tourism e-commerce platform
  10. 2021-05-04 hot news
  11. HttpSession对象与Cooike的关系 以及 Cookie对象构造函数问题
  12. gRPC-Web:替代REST的gRPC的Javascript库包
  13. The relationship between httpsession object and cooike and the construction of cookie object
  14. Grpc Web: a JavaScript library package to replace rest grpc
  15. Building reactive rest API with Java - kalpa Senanayake
  16. PDF转HTML工具——用springboot包装pdf2htmlEX命令行工具
  17. Pdf to HTML tool -- Wrapping pdf2htmlex command line tool with springboot
  18. PDF转HTML工具——用springboot包装pdf2htmlEX命令行工具
  19. Pdf to HTML tool -- Wrapping pdf2htmlex command line tool with springboot
  20. Vue.js比jQuery更容易学习
  21. Node.js的Reactor模式 与异步编程
  22. Vue. JS is easier to learn than jQuery
  23. Reactor mode of node.js and asynchronous programming
  24. 详解JavaScript中的正则表达式
  25. Explain regular expressions in JavaScript
  26. 详解JavaScript中的正则表达式
  27. Explain regular expressions in JavaScript
  28. JS: closure
  29. Write your own promise in promises / A + specification
  30. Analysis of the core mechanism of webpack from loader, plugin to egg
  31. On the import and export of webpack
  32. Interpretation of lodash source code (2)
  33. Hexo series (5) writing articles
  34. 有人用过JMeter或用HttpUnit写过测试吗????
  35. Has anyone ever used JMeter or written tests in httpUnit????
  36. JavaScript异步编程4——Promise错误处理
  37. Leetcode 1846. Reduce and rearrange the largest element of an array
  38. JavaScript asynchronous programming 4 -- promise error handling
  39. SQLite是一种经典的无服务器Serverless
  40. 通过Spring Boot Webflux实现Reactor Kafka
  41. SQLite is a classic server less
  42. Realization of reactor Kafka through spring boot Webflux
  43. Focus on the basic knowledge of JS
  44. Node.js与Spring Boot比较? - Ryan Gleason
  45. Compare node.js with spring boot- Ryan Gleason
  46. 「HTML+CSS」自定义加载动画【049】
  47. 「HTML+CSS」自定义加载动画【048】
  48. 「HTML+CSS」--自定义加载动画【047】
  49. "HTML + CSS" custom loading animation [049]
  50. "HTML + CSS" custom loading animation [048]
  51. "HTML + CSS" -- custom loading animation [047]
  52. 使用Akka实现Reactive DDD
  53. Prototype与JQuery对比
  54. Using akka to realize reactive DDD
  55. Comparison between prototype and jquery
  56. Please elaborate the diff algorithm of Vue
  57. On the combination of ecarts and Baidu map, in the Intranet environment to develop offline map, to achieve point, line, range value.
  58. 使用Slonik框架基于Node.js和PostgreSQL处理大量数据
  59. Using slonik framework to process large amount of data based on node.js and PostgreSQL
  60. Netflix使用React制作高性能电视用户界面