◆ webpack では loader が返す結果が JavaScript として正しくないといけない
  ◆ webpack 側でバンドルの変換のためかチェックされる
◆ babel-loader で新構文を transform なしで syntax プラグインのみで使うとエラー
  ◆ webpack 側で新構文に対応してない
  ◆ webpack 側の構文を追加するプラグイン設定はなさそう
◆ Parcel ならできるけど build で minify する (Terser 通す) 場合は無理な場合がある
  ◆ BigInt は Github の最新版では対応済みなのでもうすぐできそう

Chrome で使えるようになった JavaScript の新構文でも Babel の標準ではまだ対応してないものもあります
BigInt とかクラスプロパティなどがそれにあたります

こういったのを含めたい場合 Webpack では変換必須のようでした

last 1 Chrome version だけ

まずは babel-loader にプラグインなしで preset-env に最新版の Chrome を指定してやってみます

[webpack.config.js]
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: __dirname,
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", { targets: "last 1 Chrome version" }]],
},
},
},
],
},
}

変換する対象は BigInt 構文を使ったコードです
インポートもエクスポートもなしの 1 ファイルだけです

[index.js]
console.log(10n + 20n)

これで webpack コマンドを実行するとエラーになりました

ERROR in ./index.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:\Users\user\Desktop\0030\index.js: Identifier directly after number
(1:14)

> 1 | console.log(10n + 20n)
| ^
2 |

構文対応していないので n のところで構文エラーです

BigInt の構文プラグインを追加する

構文をサポートするパッケージの @babel/plugin-syntax-bigint をインストールして options に plugins 行を追加します

[webpack.config.js]
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: __dirname,
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", { targets: "last 1 Chrome version" }]],
plugins: ["@babel/plugin-syntax-bigint"],
},
},
},
],
},
}

index.js は一緒です
これに webpack コマンドを実行すると

ERROR in ./index.js 1:14
Module parse failed: Identifier directly after number (1:14)
File was processed with these loaders:
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
> console.log(10n + 20n);

やっぱりエラーです
同じ場所でパースエラーが出ていますが メッセージが少し違います
なので指定ミスではなくて一応プラグインは使用されてるはずです

babel 自体を見てみる

babel 側に問題があるのでしょうか?
構文サポートは AST 作れるだけで変換処理には変換しなくても変換用プラグインが必要とか
でも webpack の場合は変換プラグインを入れると変換されてしまいます

とりあえず babel を直接使ってみました

> const bc = require("@babel/core")
> bc.transform("1n + 20n", {plugins: ["@babel/plugin-syntax-bigint"]}).code
'1n + 20n;'

普通に変換できてますね
プラグインをはずすと

> bc.transform("1n + 20n", {}).code
Thrown:
[SyntaxError: unknown: Identifier directly after number (1:1)

> 1 | 1n + 20n
| ^
] {
pos: 1,
loc: Position { line: 1, column: 1 },
code: 'BABEL_PARSE_ERROR'
}

エラーです

webpack と同じように presets を指定すると

> bc.transform("1n + 20n", {plugins: ["@babel/plugin-syntax-bigint"], presets: [["@babel/preset-env", {targets: "last 1 Chrome version"}]]}).code
'"use strict";\n\n1n + 20n;'

「use strict」 がつきましたが問題なしです

babel-loader の問題?

babel 自体は問題なかったので babel を使う babel-loader が悪いのかも知れません
コードを全部見ていくのは面倒だったので loader の最後の処理に console.log を追加してみました

babel-loader の index.js にこういう処理があります

    const callback = this.async();
loader.call(this, source, inputSourceMap, overrides).then(args => callback(null, ...args), err => callback(err));

非同期で loader の結果を返すところです
この callback に console.log を追加します

    const callback_1 = this.async();
const callback = (...a) => { console.log(...a); callback_1(...a) }

これで結果を見てみると……

null console.log(10n + 20n); null

普通に結果が返ってますね
となると loader は問題ないので それより先の webpack 側の問題となります

loader の結果は変換済みじゃないとダメ

考えられるのは loader の結果は loader 側で保証するのでなんでもいい というわけではなく webpack 側で再チェックしてるということです

簡単なテスト用 loader を作って試してみます

[webpack.config.js]
module.exports = {
mode: "development",
entry: "./index.js",
output: {
path: __dirname,
filename: "bundle.js",
},
module: {
rules: [
{
test: /\.test$/,
use: {
loader: "./test-loader.js",
},
},
],
},
}

.test というファイルは test-loader を使います

[test-loader.js]
module.exports = () => `console.log(1n + 2n)`

test-loader はファイル問わず BigInt を使った JavaScript を出力します

[index.js]
import x from "./index.test"

console.log(x)

index.js は index.test をロードします

[index.test]
dummy

index.test はなんでもいいので dummy とだけ書いておきます

これを実行すると

ERROR in ./index.test 1:13
Module parse failed: Identifier directly after number (1:13)
File was processed with these loaders:
* ./test-loader.js
You may need an additional loader to handle the result of these loaders.
> console.log(1n + 2n)
@ ./index.js 1:0-28 3:12-13

同じエラーです

test-loader から n を外して普通の足し算にすると

[test-loader.js]
module.exports = () => `console.log(1 + 2)`

エラーはなくなりました

webpack は変換必須みたい

そういえば プラグイン追加後のときもエラーメッセージがこういう文章でした

You may need an additional loader to handle the result of these loaders.

webpack 側で JavaScript として構文チェックして正しくないときに出してるメッセージのようです
loader の実装ミスや使う側の設定で JavaScript じゃないものをそのまま返してしまってるミスもありそうですし 親切なのかもしれません
それだけじゃなくて バンドルのための変換は単純な正規表現で置換ではなく JavaScript として構文をパースして置き換えてるからなのかもしれません

何にしても webpack だと新構文は変換なしに出力できないようです
使ってる Node.js のバージョンでは BigInt は普通に使えるので Node.js 依存ということはさすがになさそうです
たぶん webpack の JavaScript パースも Babel だとは思うのですけど こっち側には babel-loader みたいな設定はできなさそうです
babel-loader があるならそれ使って欲しいものですが loader の設定は webpack 本体とは独立してるので babel-loader の設定を使うというのも無理そうです
一応設定ファイルなどをいくつかみたり ググったりしても特に見つからないですしやっぱり無理なんだと思います
大抵の場合は変換するのであまり需要もないでしょうしね

とは言っても 変換は不要でただバンドルだけして 1 ファイルにまとめたいだけってことはあります
そのときに余計なことはしてほしくないです

Parcel ならできる (Terser 次第)

新機能なら Parcel かな と思ってやってみると普通にできました
ただし minify しない場合のみ

build コマンドを使うと Terser を使った minify が行われますがここでエラーになります
serve など minify しないバンドル処理は問題なしです

BigInt の場合は Terser も github 上では PR がすでマージされてるのでしばらくすれば Parcel から使えるようになると思います
7 月のはじめのころなのでまだマージ後のリリースもでてないのかもしれません
https://github.com/terser-js/terser/pull/325

Parcel を使わなくなった一番の理由は node_modules に babel を通さないことなので こういう Chrome のみみたいなバンドルだと Parcel で困らないですし Parcel で良いかもしれません

続き

続いた