Webpack 插件静态分析导出函数的使用情况?

j_d*_*j_d 4 javascript optimization static-analysis node.js webpack

首先,请注意,我是故意设置这个假设问题的,因为这是我面临解决的现实世界问题,并且不确定它是否可能。

假设我有一个名为 的 JavaScript 包,road-fetcher带有一个名为 的导出函数find,我可以将纬度/经度坐标传递到其中,并返回到该点的最近的道路。

所以:

import { find } from 'road-fetcher'

find(36.585940, -95.304241) // output: 'US Route 66'
Run Code Online (Sandbox Code Playgroud)

假设使用我的road-fetcher应用程序的用户最终find在其 Web 应用程序中使用此函数 200-300 倍(所有这些都是以可预测的、不可预测的Math.random方式)。

在内部,我的road-fetcher包正在向我的外部 API 发出网络请求。这工作得很好,但如果我们在运行时继续这样做,我们会在每个客户端上产生带宽和延迟成本(无论是在浏览器还是服务器中),但我们不一定需要。而且,也许所有 200 个调用都在同一页面上。相反,理想的做法是在构建时生成初始值,并可能设置一些长 TTL 以便稍后重新验证值。

使用 Webpack 处理 JavaScript 是很常见的,我想知道是否可以静态分析用户对该find函数的使用情况以找到所有排列。

理想情况下,我希望编译带有参数的函数调用的总列表,然后在构建步骤中基于此发出网络请求,但甚至能够编译所有函数调用参数的列表并将其存储在文件系统(不确定是在node_modules或 cwd 内),以促进单个缓存热步骤也足够了。

初步查看 Webpack 文档,这似乎可能一个起点,但我对此的了解还不够深入。evaluateCallExpressionMember

我很欣赏这是一个人为的例子,但它确实代表了一个非常现实的问题,我试图在这里简化以清楚地隔离手头的问题。

Jac*_*yef 5

我设法编写了一个 webpack 插件,它接受函数名称和将使用传递给原始函数的参数调用的回调。

\n
\n

这是回购协议如果您只想跳入代码,

\n
\n

它有效,但有一些限制:

\n
    \n
  1. 它仅适用于 ES 模块
  2. \n
  3. 如果命名导入被重命名(例如:import { log as logg } from \'./something\'
  4. \n
  5. 如果传递给函数的参数是变量,则不起作用
  6. \n
\n

结果

\n

这是我的源代码:

\n
import { log } from \'./helpers/log\';\n\n// these will be logged\nlog(1);\nlog(2, 3);\nlog(2, "foo");\nlog(2, "foo", 4, "bar");\nlog(2, "foo", 4, "bar", [1, 2, "asd"]);\nlog(2, "foo", 4, "bar", [1, 2, "asd"], { foo: "bar" }, [\n  { a: "asd", b: 123, c: [] },\n]);\n\n// this one will not be logged because it\'s using a variable\nconst a = [1,2,3];\nlog(a);\n\n// this one will also not be logged because it\'s not using `log` exactly\nconsole.log(\'asd\');\n
Run Code Online (Sandbox Code Playgroud)\n

这是 webpack 配置:

\n
const FunctionCallPlugin = require(\'./webpack-plugins/FunctionCall\');\n\nmodule.exports = {\n  plugins: [ new FunctionCallPlugin({ functionName: \'log\', callback: ({ arguments: args }) => {\n    console.log(\'`log` function was found and called with the arguments:\', args);\n    // you can do whatever here, make a http request, write to db, etc\n  }})],\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这是运行 webpack 时的终端输出:

\n
\xe2\x9e\x9c  webpack-plugin-function-invoke git:(master) \xe2\x9c\x97 yarn webpack\nyarn run v1.16.0\nwarning ..\\package.json: No license field\n$ D:\\Projects\\webpack-plugin-function-invoke\\node_modules\\.bin\\webpack\n`log` function was found and called with the arguments: [ 1 ]\n`log` function was found and called with the arguments: [ 2, 3 ]\n`log` function was found and called with the arguments: [ 2, \'foo\' ]\n`log` function was found and called with the arguments: [ 2, \'foo\', 4, \'bar\' ]\n`log` function was found and called with the arguments: [ 2, \'foo\', 4, \'bar\', [ 1, 2, \'asd\' ] ]\n`log` function was found and called with the arguments: [\n  2,\n  \'foo\',\n  4,\n  { foo: \'bar\' },\n  [ { a: \'asd\', b: 123, c: [] } ]\n]\n
Run Code Online (Sandbox Code Playgroud)\n

插件如何工作

\n
    \n
  1. 它循环遍历所有 webpack 块以及每个块内的所有模块,以查看它们是否具有指定名称的任何依赖项。例如,如果您正在寻找log,它将找到任何执行以下操作的模块import { log } from \'./somewhere\'import log from \'./somewhere\'
  2. \n
  3. 对于具有指定依赖项的每个模块,它通过查看其原始源代码来收集所有文件路径fileDependencies
  4. \n
  5. 每个文件的源代码都被读取为字符串fs模块读取为字符串
  6. \n
  7. 使用 babel 解析器将源代码解析为 AST
  8. \n
  9. AST 被遍历,我们调查所有CallExpression使用相同指定函数名称的内容
  10. \n
  11. 我们收集函数调用中使用的所有参数
  12. \n
  13. 我们运行指定的回调,同时传递收集到的参数
  14. \n
\n

就是这样!它可能不是那么高效,但是嘿它有效:)

\n

我已将其放入GitHub 存储库中如果您对代码感兴趣,我知道这不是您问题的完整答案,但希望对您有所帮助!

\n