首页 > 其他分享 >分析web应用内引用依赖的占比

分析web应用内引用依赖的占比

时间:2023-04-13 19:34:31浏览次数:34  
标签:files web 依赖 resource parser source webpack 引用 import

背景

针对目前团队自己开发的组件库,对当前系统内引用组件库占比进行统计分析,以实现对当前进度的总结以及后续的覆盖度目标制定。

主要思路

目前找到的webpack分析插件,基本都是针对打包之后的分析打包之后的chunk进行分析,但是我希望的是分析每个页面中的import数,对比一下在所有页面中的import数中有多少是使用了组件库的。所以就在网上看了一些相关资料以及webpackapi文档。主要是利用webpackimportCallimportimportSpecifier三个钩子来实现,它们的作用直接跟着代码看一下。

完整代码实现

import fs from 'fs';
import path from 'path';
import resolve from 'enhanced-resolve';

let myResolve;

/**
 * 通过source获取真实文件路径
 * @param parser
 * @param source
 */
function getResource(parser, source) {
  if (!myResolve) {
    myResolve = resolve.create.sync(parser.state.options.resolve);
  }
  let result = '';
  try {
    result = myResolve(parser.state.current.context, source);
  } catch (err) {
    console.log(err);
  } finally {
    return result;
  }
}

class WebpackImportAnalysisPlugin {
  constructor(props) {
    this.pluginName = 'WebpackCodeDependenciesAnalysisPlugin';
    //  文件数组
    this.files = [];
    //  当前编译的文件
    this.currentFile = null;
    this.output = props.output;
  }

  apply(compiler) {
    compiler.hooks.compilation.tap(this.pluginName, (compilation, { normalModuleFactory }) => {
      const collectFile = parser => {
        const { rawRequest, resource } = parser.state.current;
        if (resource !== this.currentFile) {
          this.currentFile = resource;
          this.files.push({
            name: rawRequest,
            resource,
            children: []
          });
        }
      };
      const handler = parser => {
        // 用来捕获import(xxx)
        parser.hooks.importCall.tap(this.pluginName, expr => {
          collectFile(parser);
          let ast = {};
          const isWebpack5 = 'webpack' in compiler;
          // webpack@5 has webpack property, webpack@4 don't have the property
          if (isWebpack5) {
            // webpack@5
            ast = expr.source;
          } else {
            //webpack@4
            const { arguments: arg } = expr;
            ast = arg[0];
          }
          const { type, value } = ast;
          if (type === 'Literal') {
            const resource = getResource(parser, value);
            this.files[this.files.length - 1].children.push({
              name: value,
              resource,
              importStr: `import ('${value}')`
            });
          }
        });
        // 用来捕获 import './xxx.xx';
        parser.hooks.import.tap(this.pluginName, (statement, source) => {
          // 由于statement.specifiers.length大于0的时候同时会被importSpecifier钩子捕获,所以需要在这个地方拦截一下,这个地方只处理单独的引入。
          if (statement.specifiers.length > 0) {
            return;
          }
          collectFile(parser);
          this.files[this.files.length - 1].children.push({
            name: source,
            resource: getResource(parser, source),
            importStr: `import '${source}'`
          });
        });
        // 用来捕获 import xx from './xxx.xx';
        parser.hooks.importSpecifier.tap(
          this.pluginName,
          (statement, source, exportName, identifierName) => {
            collectFile(parser);
            let importStr = '';
            if (exportName === 'default') {
              importStr = `import ${identifierName} from '${source}'`;
            } else {
              if (exportName === identifierName) {
                importStr = `import { ${identifierName} } from '${source}'`;
              } else {
                importStr = `import { ${exportName}: ${identifierName} } from '${source}'`;
              }
            }
            this.files[this.files.length - 1].children.push({
              name: source,
              exportName,
              identifierName,
              importStr,
              resource: getResource(parser, source)
            });
          }
        );
      };

      normalModuleFactory.hooks.parser.for('javascript/auto').tap(this.pluginName, handler);
    });

    compiler.hooks.make.tap(this.pluginName, compilation => {
      compilation.hooks.finishModules.tap(this.pluginName, modules => {
        // 过滤掉深度遍历的node_modules中的文件,只分析业务代码中的文件
        const needFiles = this.files.filter(
          item => !item.resource.includes('node_modules') && !item.name.includes('node_modules')
        );
        fs.writeFile(this.output ?? path.resolve(__dirname, 'output.json'), JSOn.stringify(needFiles, null, 4), err => {
          if (!err) {
            console.log(`${path.resolve(__dirname, 'output.json')}写入完成`);
          }
        });
      });
    });
  }
}

export default WebpackImportAnalysisPlugin;
// 以文件为基准,扁平化输出所有的import
[
  {
    "name": "./src/routes",
    "resource": "/src/routes.tsx",
    "children": [
      {
        "name":"react",
        "exportName":"lazy",
        "identifierName":"lazy",
        "importStr":"import { lazy } from 'react'",
        "resource":"/node_modules/.pnpm/[email protected]/node_modules/react/index.js"
      },
    ...  
    ]
  },
  ...
]

后续

上面拿到的数据是扁平化的数据,如果针对需要去分析整体的树状结构,可以直接将扁平化数据处理一下,定义一个主入口去寻找它的子级,这样可以自己去生成一颗树状的import关系图。

标签:files,web,依赖,resource,parser,source,webpack,引用,import
From: https://www.cnblogs.com/aloneMing/p/17316100.html

相关文章

  • day12-Web登录认证
    案例-登录认证在前面的课程中,我们已经实现了部门管理、员工管理的基本功能,但是大家会发现,我们并没有登录,就直接访问到了Tlias智能学习辅助系统的后台。这是不安全的,所以我们今天的主题就是登录认证。最终我们要实现的效果就是用户必须登录之后,才可以访问后台系统中的功能。1.......
  • PentestLab-web安全XML测试-EXP2
    我们打开靶机选择“example2”观察页面我们尝试修改name参数http://192.168.20.157/xml/example2.php?name=hacker%27添加单引号报错存在闭合的可能性网上大神payload为hacker'or1=1]/parent::*/child::node()%00完整的payload为http://192.168.20.157/xml/example2.php?name=ha......
  • javaweb验证码
    publicclassmyfunction{publicstaticStringgetRandString(intlength){Stringstr="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";Randomrandom=newRandom();StringBuffersb=newStringBuffe......
  • Go For Web:踏入Web大门的第一步——Web 的工作方式
    前言:本文作为解决如何通过Golang来编写Web应用这个问题的前瞻,对Golang中的Web基础部分进行一个简单的介绍。目前Go拥有成熟的Http处理包,所以我们去编写一个做任何事情的动态Web程序应该是很轻松的,接下来我们就去学习了解一些关于Web的相关基础,了解一些概念,以及......
  • 【web 开发基础】PHP 中的特殊流程控制(continue) -PHP 快速入门 (21)
    continue语句continue语句只能用于循环语句内部,功能是跳过本次循环继续执行下一次循环结构,而不终止整个循环的执行。在while和do...while语句中,continue语句跳转到循环条件处开始继续执行,对于for循环随后的动作是变量更新。流程图如下:continue的语法如下:continue[label];可选的......
  • golang 指针自动解引用
    在Go中,对指针类型进行解引用操作可以使用星号(*)运算符。如果要自动解引用一个指针,可以使用点号(.)运算符来访问其字段或方法,Go会在必要时自动解引用该指针。例如,假设有一个结构体类型的指针变量p,其中包含一个整数字段x:typemyStructstruct{xint}funcmain(){......
  • springboot 整合 webservice服务
    目录webservice百科创建一个springboot项目并导入相关依赖编写webservice接口编写实现类发布webservice浏览器访问postman调用在前一段时间用到了webservice服务,今天整理了一下,记录下来。webservice百科WebService是一个平台独立的,低耦合的,自包含的、基于可......
  • 微信小程序嵌套的webview页面实现导航,可跳转高德百度等app
    需求:微信小程序中使用webview嵌套了h5页面,h5页面中有经纬度,需要实现唤醒高德app。实现思路:此处h5未配置wxSDK,无法直接使用openLocation,所以将经纬度传给小程序,map页面为跳转中间页,再实现打开地图页面。h5://高德地图导航constaMapNavigate=(address,lng,lat)=>{......
  • webServlet添加数据报错500
     新建数据库文件,之后跳转出现问题,发现是数据库问题。字符不匹配 代码里是utf-8,将字符修改为utf-8,顺序为utf8之后可以 ......
  • 微信小程序 web-view在校验合法域名后视频不能播放
    异常 注:视频资源在小程序的服务器上  原因:web-view请求的外网资源需要在小程序的后台配置业务域名才能访问,并将验证文件放置在服务器根目录下在配置业务域名时遇到一个问题是,业务域名配置不能带端口,请求的服务器资源的域名带端口,校验文件没法放置到指定域名位置下,后又在......