|
背景
- 针对目前团队自己开发的组件库,对当前系统内引用组件库占比进行统计分析,以实现对当前进度的总结以及后续的覆盖度目标制定。
复制代码 主要思路
目前找到的webpack分析插件,基本都是针对打包之后的分析打包之后的chunk进行分析,但是我希望的是分析每个页面中的import数,对比一下在所有页面中的import数中有多少是使用了组件库的。所以就在网上看了一些相关资料以及webpack的api文档。主要是利用webpack的importCall、import、importSpecifier三个钩子来实现,它们的作用直接跟着代码看一下。
完整代码实现
- 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/react@17.0.2/node_modules/react/index.js"
- },
- ...
- ]
- },
- ...
- ]
复制代码 后续
上面拿到的数据是扁平化的数据,如果针对需要去分析整体的树状结构,可以直接将扁平化数据处理一下,定义一个主入口去寻找它的子级,这样可以自己去生成一颗树状的import关系图。
来源:https://www.cnblogs.com/aloneMing/p/17316100.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
|