javascript - Webpack 插件静态分析导出函数的使用情况?
问题描述
首先,请注意我是故意设置这个假设问题,因为这是我面临解决的现实世界问题,甚至不确定它是否可能。
假设我有一个名为 的 JavaScript 包,road-fetcher
其中有一个名为 的导出函数find
,我可以将 lat/lng 坐标传递到该函数中,然后返回到该点最近的道路。
所以:
import { find } from 'road-fetcher'
find(36.585940, -95.304241) // output: 'US Route 66'
假设使用我的road-fetcher
应用程序的用户最终在他们的网络应用程序中使用此find
功能 200-300 倍(所有这些都是可预测的,非Math.random
方式)。
在内部,我的road-fetcher
包正在向我的外部 API 发出网络请求。这很好,但是如果我们在运行时继续这样做,我们会在每个客户端上产生带宽和延迟成本(无论是在浏览器还是服务器中),我们不一定需要。此外,也许所有 200 个电话都在同一页面上。相反,最好在构建时生成初始值,并可能设置一些较长的 TTL 以便稍后重新验证值。
使用 Webpack 处理 JavaScript 是司空见惯的,我想知道是否可以静态分析用户对该find
函数的使用以找到所有排列。
理想情况下,我想用它们的 args 编译函数调用的总列表,然后在构建步骤期间基于此发出网络请求,但甚至能够编译所有函数调用 args 的列表并将其存储在某处文件系统(不确定,withinnode_modules
或 cwd),以促进单个缓存热步骤也足够了。
初步了解一下 Webpack 文档,这似乎是一个起点,但我在这里的深度不够。evaluateCallExpressionMember
我很欣赏这是一个人为的例子,但它确实代表了一个非常现实的问题,我在这里试图简化它以清楚地隔离手头的问题。
解决方案
我设法编写了一个接受函数名称的 webpack 插件,以及一个将使用传递给原始函数的参数调用的回调。
如果您只想跳入代码,这里是repo
它可以工作,但有一些限制:
- 它仅适用于 ES 模块
- 如果命名导入被重命名(例如:
import { log as logg } from './something'
- 如果传递给函数的参数是变量,则它不起作用
结果
这是我的源代码:
import { log } from './helpers/log';
// these will be logged
log(1);
log(2, 3);
log(2, "foo");
log(2, "foo", 4, "bar");
log(2, "foo", 4, "bar", [1, 2, "asd"]);
log(2, "foo", 4, "bar", [1, 2, "asd"], { foo: "bar" }, [
{ a: "asd", b: 123, c: [] },
]);
// this one will not be logged because it's using a variable
const a = [1,2,3];
log(a);
// this one will also not be logged because it's not using `log` exactly
console.log('asd');
这是 webpack 配置:
const FunctionCallPlugin = require('./webpack-plugins/FunctionCall');
module.exports = {
plugins: [ new FunctionCallPlugin({ functionName: 'log', callback: ({ arguments: args }) => {
console.log('`log` function was found and called with the arguments:', args);
// you can do whatever here, make a http request, write to db, etc
}})],
}
这是运行 webpack 时的终端输出:
➜ webpack-plugin-function-invoke git:(master) ✗ yarn webpack
yarn run v1.16.0
warning ..\package.json: No license field
$ D:\Projects\webpack-plugin-function-invoke\node_modules\.bin\webpack
`log` function was found and called with the arguments: [ 1 ]
`log` function was found and called with the arguments: [ 2, 3 ]
`log` function was found and called with the arguments: [ 2, 'foo' ]
`log` function was found and called with the arguments: [ 2, 'foo', 4, 'bar' ]
`log` function was found and called with the arguments: [ 2, 'foo', 4, 'bar', [ 1, 2, 'asd' ] ]
`log` function was found and called with the arguments: [
2,
'foo',
4,
{ foo: 'bar' },
[ { a: 'asd', b: 123, c: [] } ]
]
插件的工作原理
- 它遍历所有 webpack 块,以及每个块中的所有模块,以查看它们是否具有具有指定名称的任何依赖项。例如,如果您正在寻找
log
,它将找到任何import { log } from './somewhere'
模块import log from './somewhere'
- 对于每个具有指定依赖关系的模块,它通过查看其收集原始源代码的所有文件路径
fileDependencies
fs
使用模块将每个文件的源代码读取为字符串- 使用 babel 解析器将源代码解析为 AST
- 遍历 AST,我们查看所有
CallExpression
使用相同指定函数名称的函数 - 我们收集函数调用中使用的所有参数
- 我们运行指定的回调,同时传递收集的参数
而已!它可能不是那么高效,但是它可以工作:)
如果您对代码感兴趣,我已将其放在GitHub 存储库中。我知道这不是您问题的完整答案,但希望对您有所帮助!
推荐阅读
- c++ - 当计算机在我的代码中要求输入数组中存在的键时,如何打印“数组中不存在键(元素)”
- user-interface - 带有 GUI 界面的 ACL 管理
- javascript - 以动态形式隐藏空输入
- javascript - 类不是 Safari(iOS 和 MacOS)中新事件的构造函数
- react-native - 管理推送通知
- python - 无法从传输连接中读取数据 - 来自 python 的 C# 端口
- laravel - curl请求中的Xero授权不成功错误
- javascript - 为什么 Jest 给出 ReferenceError 的?
- sql - 如何在使用触发器插入之前成功引用另一个表
- javascript - PDFTron 横向绘制文本