Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node

  • VSCode插件

  • Webpack

    • webpack源码分析一:AST语法
      • 0.前言
      • 1.什么是AST,及AST的应用
      • 2.AST树操作
      • 3. Babel 家族简介
      • 4.使用AST树:分析 Webpack 模块依赖
      • 5.总结:
      • 6.参考资料:
    • webpack源码分析二:模块化语法
    • webpack源码分析三:源码分析
    • webpack 配置[TODO]
    • 如何利用requireContext实现批量导入
  • Redux

  • React源码

  • 组件库

  • React高阶系列

  • UMI插件

  • 前端工程化

  • 单元测试vitest

  • 重点技术
  • Webpack
wangjiasheng
2022-03-27
目录

webpack源码分析一:AST语法

# 0.前言

本篇博客为webpack_deom01 (opens new window)项目仓库笔记,主要有以下内容:

  1. AST 的含义及其应用
  2. Babel中操作AST 树的API说明。
  3. 使用AST 操作完成两个案例:
    • 将 let 转化为 var。
    • 将 import 导入的模块保存在数组中(depRelation),实现对模块的静态分析。

# 1.什么是AST,及AST的应用

AST 是 Abstract Syntax Tree 的简称,是前端工程化绕不过的一个名词。它涉及到工程化诸多环节的应用,比如:

  1. 如何将 Typescript 转化为 Javascript (typescript)
  2. 如何将 SASS/LESS 转化为 CSS (sass/less)
  3. ⭐️如何将 ES6+ 转化为 ES5 (babel)
  4. 如何将 Javascript 代码进行格式化 (eslint/prettier)
  5. 如何识别 React 项目中的JSX(babel)
  6. ❓GraphQL、MDX、Vue SFC 等等

而在语言转换的过程中,实质上就是对其 AST 的操作,核心步骤就是 AST 三步走

  1. Code -> AST (Parse)

  2. AST -> AST (Transform)

  3. AST -> Code (Generate)

在案例1中使用babel完成AST转换,实现变量声明(VariableDeclaration)语法的降级(将let改为var)。

初始code为:

const code = `let a = 'let'; let b = 2`;
1

let a = 'let' 将被解析为以下的AST 树结构:

"body": [
  {
    "type": "VariableDeclaration",
    "declarations": [
      {
        "type": "VariableDeclarator",
        "id": {
          "type": "Identifier",
          "name": "a"
        },
        "init": {
          "type": "StringLiteral",
          "extra": {
            "rawValue": "let",
            "raw": "'let'"
          },
          "value": "let"
        }
      }
    ],
    "kind": "let"
  },
  ...
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

上述 AST 基本将 code 的行为做一个很好的表征:

  • 等式左侧:

    • 变量声明:type: "VariableDeclaration"
    • 类型:kind: "let"
  • 等式右侧:

    • 值的类型:字符串 StringLiteral、数值 NumericLiteral
    • 值为:value: "let"

# 2.AST树操作

这部分主要借助的Babel库提供的三个底层的API:

import { parse } from "@babel/parser"
import traverse from "@babel/traverse"
import generate from "@babel/generator"
1
2
3

转化操作:

const code = `let a = 'let'; let b = 2`;

// parse: Code => AST(引用地址)
const ast = parse(code, { sourceType: "module" });

// traverse : AST => Code 
// 无输出,直接修改的原 AST 树
traverse(ast, {
  enter: (item) => {
    if (item.node.type === "VariableDeclaration") {
      if (item.node.kind === "let") {
        item.node.kind = "var";
      }
    }
  },
});

// generate
const result = generate(ast, {}, code);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3. Babel 家族简介

一般情况而言,我们不会直接使用parser、traverse、generator这三个 Api 对 AST 树进行操作,Babel 已经封装好更为简洁的操作AST树的API,这些库封装在@babel/core ,主要提供以下两类AST操作能力:

  1. 可以直接生成 code 的 API:

    • 从 code 转化为 code

      • transform 、transformSync
    • 从 file 转化为 code

      • transformFile、transformFileSync:大部分情况下,code的形式不是以字符串的形式,而是需要通过fs.readFile的形式读取的,具体见案例3。
    • 从 Ast 转化为 code

      • transformFromAst、transformFromAstSync:
  2. 与解析相关的 API:

    • parse :仅支持 babel v7 之前的版本,v8 不再对该 api向后兼容。
    • 新版请使用:parseSync 和 parseAsync

详细,各APi的使用见:@babel/core (opens new window) 官方文档

上述api在使用时,还可在转化时设置option,如在babel中内置着许多的preset(预制)

const result = babel.transformFromAstSync(ast, es6Code, {
  presets: ["@babel/preset-env"],
});
1
2
3

其中, @babel/preset-env 是一个智能预设,允许当前环境使用最新的 JavaScript而不需要针对目标宿主环境进行额外的调整,preset-env将会所有 ES2015-ES2020 代码自动转化为 ES5 代码。

具体实践见:案例3

# 4.使用AST树:分析 Webpack 模块依赖

在【案例4】中,存在 4 个 project文件夹,分别对应着以下几种情况:

  1. project_1与deps.ts文件:基础模块分析实现。
  2. project_2与deps2.ts文件:递归分析深层依赖。
  3. project_3与deps3.ts文件:可以对循环依赖进行静态分析。

核心实现原理:

检查代码中是否有 import "./xxx.js" 这段代码,也即 AST 树中搜索 path.node.type 是否有ImportDeclaration类型。如果存在则将path.node.source.value中的内容存入depRelation数组中。

traverse(ast, {
    enter: (path) => {
      if (path.node.type === "ImportDeclaration") {
        // path.node.source.value 往往是一个相对路径,如 ./a.js,需要先把它转为一个绝对路径
        const depAbsolutePath = resolve(
          dirname(filepath),
          path.node.source.value
        );
        // 然后转为项目路径
        const depProjectPath = getProjectPath(depAbsolutePath);
        // 把依赖写进 depRelation
        depRelation[key].deps.push(depProjectPath);
        collectCodeAndDeps(depAbsolutePath);
      }
    },
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 5.总结:

  1. AST相关:

    • parse: 把代码 code 变成 AST。
    • traverse: 遍历 AST进行修改。
    • generate: 把 AST 变成代码 code2。
  2. 工具:

    • @babel/parser
    • @babel/traverse
    • @babel/generator
    • @babel/core 包含前三者
    • @babel/preset-env 内置了ES6转ES5的很多规则
  3. 构建了一个 dep.ts 函数实现对模块的依赖分析。

# 6.参考资料:

  1. 程序员山月 (opens new window)
编辑 (opens new window)
上次更新: 2022/04/06, 15:04:00
VSCode插件-开发 code-snippets 插件
webpack源码分析二:模块化语法

← VSCode插件-开发 code-snippets 插件 webpack源码分析二:模块化语法→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式