xiaoxiaojx / blog

Project for records problems solved in my work and study.
https://xiaoxiaojx.github.io/
MIT License
253 stars 6 forks source link

Module parse failed: Unexpected token 问题记录 #28

Open xiaoxiaojx opened 2 years ago

xiaoxiaojx commented 2 years ago

91530678e446bb4a16d5ec3e311fd81773657d3d

报错信息

Module parse failed: Unexpected token (1:6518)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders

报错分析

一开始注意力都在 You may need an appropriate loader to handle this file type 这个报错信息上, 通常这个错误是因为 ts、css、png 等非 js 文件没有设置对应的 loader。但是这里看上去是一个打包好的 umd 的 js 文件, 按理来说 webpack 能识别 js 文件。

真正的错误

其实真正的错误信息是第一句 Module parse failed: Unexpected token (1:6518), 找到报错定位的代码发现有如下可选链操作符 ?. 的语法

let a = window?.b

所以真正的原因为 webpack 进行依赖分析的 AST 遍历时未能识别可选链操作符的语法, 即下面的 parse 函数抛错

// webpack/lib/NormalModule.js

try {
    const result = this.parser.parse(
        this._ast || this._source.source(),
        {
            current: this,
            module: this,
            compilation: compilation,
            options: options
        },
        (err, result) => {
            if (err) {
                handleParseError(err);
            } else {
                handleParseResult(result);
            }
        }
    );
    if (result !== undefined) {
        // parse is sync
        handleParseResult(result);
    }
} catch (e) {
    handleParseError(e);
}

上面的 parser 其实是 acornParser, 可以说 babel 等都是 acorn 衍生出来的。

babel/issues/11393 @babel/parser was born as a fork of Acorn, but it has been completely rewritten. However, we still use some of its algorithms (for example, to handle expressions precedence).


// webpack/lib/Parser.js

const acorn = require("acorn"); const acornParser = acorn.Parser;

const defaultParserOptions = { ranges: true, locations: true, ecmaVersion: 11, sourceType: "module", onComment: null };

通过上面的参数 ecmaVersion 可知, 当前只会识别 ES 11 (2020), 而[可选链操作符](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Optional_chaining) 还是 Stage 4 阶段
![image](https://user-images.githubusercontent.com/23253540/151685991-b3f52950-afca-42b3-aad1-b04fa6a15281.png)

## 问题解决
修改 babel-loader 的 include 字段, 将含有较高语法的 npm 包经过 babel 编译一次, 到 webpack 处理时就是兼容性良好的 e5 代码了
```js
// webpack.config.js

// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
    test: /\.(js|mjs|jsx|ts|tsx)$/,
    include: paths.appSrc,
    loader: require.resolve('babel-loader'),
    options: {
        // ...
        }
}