简短版:
我们如何操作最终输出包的AST,以及加载器内文件的AST?在这两种情况下,我都想操纵现有的AST,而不是我正在做的是解析源并创建一个新的AST。我做的很慢,我知道Webpack必须已经制作了一个AST,所以我想避免重复工作。
长版:
例如,假设我有一堆文件以类似于(但不完全)AMD模块的格式编写:
module({
Foo: '/path/to/Foo',
Bar: '/path/to/Bar',
Baz: '/path/to/Baz',
}, function(imports) {
console.log(imports) // {Foo:..., Bar:... Baz:...}
})
不同之处在于它被称为module
而不是define
,依赖关系参数是导入名称到模块路径而不是模块路径数组的映射,模块主体函数接收到import
对象包含所有请求的导入,而不是每个请求导入的一个参数。
以上类似于AMD格式的以下内容,具有相同的输出:
define([
'/path/to/Foo',
'/path/to/Bar',
'/path/to/Baz',
], function(Foo, Bar, Baz) {
console.log({Foo, Bar, Baz}) // {Foo:..., Bar:... Baz:...}
})
为了最终构建一个包含在此{{1}中编写的文件的捆绑包,建议使用Webpack来使Webpack能够理解文件(能够知道文件具有哪些依赖关系)的方法是什么?格式?
我已经尝试过一种方法:我创建了一个自定义加载器,它接收文件的源作为字符串,解析它并创建和AST,转换AST,然后输出AMD中的代码{{ 1}}格式,Webpack理解。
然而,我觉得这很慢,因为如果有很多文件并且它们很大,那么从每个文件解析并制作AST似乎是多余的,因为我打赌Webpack已经开始这样做了。有没有办法从Webpack获取AST并在Webpack想要扫描它的依赖项之前对其进行转换,这样我就可以将AST转换为AMD格式(或者任何公认的格式),这样Webpack就可以了使用该文件?还有另一种方法吗?
答案 0 :(得分:2)
我认为您会发现在依赖项解析期间使用了加载器。
解析器基本上需要源代码才能完成其工作。因此,在当前解析阶段遇到的任何import / require语句(依赖项)都需要:a。得到解决并且:b。在可以解析之前加载。如果你挂钩进入enhanced-resolve软件包的“resolve-step”,你可以在console.log中找出解析器转换过来的状态转换,通常最终会触发“create-module”插件。
陷入“解决步骤”:
compiler.plugin('after-resolvers', (compiler) => {
compiler.resolvers.normal.plugin('resolve-step', function (type, request){
console.log("resolve-step type:["+type+"],
path:["+request.path+"], request:["+request.request+"]");
});
});
进入“创建模块”:
compiler.plugin('compilation', (compilation, params) => {
params.normalModuleFactory.plugin("create-module", (data) => {
console.log('create-module: raw-request: '+data.rawRequest);
}
}
希望这有帮助。
答案 1 :(得分:1)
我正在寻找类似的东西,想要操纵ast,但没有示例或有用的文档。
谷歌搜索它只是浪费了我2小时的时间,但是用了一半完成且难以理解的文档,我确实提出了这个(虽然不起作用):var acorn = require("acorn-dynamic-import").default;
function MyPlugin(options) {
// Configure your plugin with options...
}
MyPlugin.prototype.apply = function(compiler) {
compiler.plugin("compilation", function(compilation, data) {
data.normalModuleFactory.plugin(
"parser"
, function(parser, options) {
parser.plugin(
[
"statement if"
]
,(node)=>
Object.assign(
node
,{
test:{
type:"Literal",
start: node.test.start,
end: node.test.start+4,
loc: {
start: {
line: 7,
column: 3
},
end: {
line: 7,
column: node.test.loc.start.column+4
}
},
range: [
node.test.range,
node.test.range+4
],
value: true,
raw: "true"
}
}
)
);
});
});
};
module.exports = MyPlugin;
这会让我成为if语句的节点,对于Parser.js可以查看的其他类型的节点。
我尝试返回另一个节点,但创建一个节点需要做很多工作,似乎没有一种简单的方法可以做到这一点。
为什么还要在webpack中尝试这样做呢? webpack贡献者已经制作了一个很好的产品,但是没有人知道它是如何工作的,因为它的许多功能都没有文档。
作为一个babel插件,你最好这样做,这个插件编写得很好且可以理解documentation。
我和巴贝尔在约20分钟内完成了这项工作:
module.exports = function ({ types: t }) {
return {
visitor: {
IfStatement(path, file) {
path.node.test = {
"type": "BooleanLiteral",
"start": path.node.test.start,
"end": path.node.test.start+4,
"loc": {
"start": {
"line": 7,
"column": path.node.test.loc.start.column
},
"end": {
"line": 7,
"column": path.node.test.loc.start.column+4
}
},
"value": true
}
// debugger;
// path.unshiftContainer('body', t.expressionStatement(t.stringLiteral('use helloworld')));
}
}
};
};
在webpack.config.js中:
const path = require("path");
const webpack = require("webpack");
module.exports = {
entry: "./src",
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].chunk.js"
},
module:{
rules: [
{
test: /\.html$/,
use: [{ loader: './plugin/templateLoader' }]
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['env'],
plugins: [require("./plugin/myBabelPlugin")]
}
},
}
]
}
}