rollup / vite で prebuild 済みのファイルをパースせずに読み込む
ビルド済みの依存がない 巨大な js を import するとき、バンドラーによる解析フェーズを飛ばしたいことがあります。巨大なファイルを別にビルドして、アプリケーションとしてその利用者になるときなどですね。単体で動く巨大なモジュールとして、 typescript や prettier が挙げられます。
というわけで、 webpack だと noparse オプションで解析をスキップできるのですが、 vite / rollup だとそれがないので無理やり実現するプラグインを作りました。
気が向いたら npm に投げますが、別に設定ファイルに直接書いてもいいぐらいの分量なので, vite.config.ts を置いときます。(vite に設定ファイルの ts 対応が入ってます)
// vite.config.ts
import type { Plugin } from "rollup";
import { defineConfig } from "vite";
const noparse = () =>
({
name: "noparse",
enforce: "pre", // for vite plugin order
buildStart: () => console.time("noparse"),
buildEnd: () => console.timeEnd("noparse"),
transform(code: string, id: string) {
if (id.endsWith("?noparse")) {
const encoded = Buffer.from(
unescape(encodeURIComponent(code))
).toString("base64");
return {
code: `const url = "data:text/javascript;base64," + "${encoded}";
const f = new Function("u", "return import(u)");
export default () => f(/* @vite-ignore */ url);`,
};
}
if (id.endsWith("?noparse-umd")) {
return {
code: `const m = { exports: {}};
new Function('module', 'exports', ${JSON.stringify(code)})(m, m.exports);
export default m.exports`,
};
}
},
} as Plugin);
export default defineConfig({
build: {
minify: false,
target: "esnext",
lib: {
entry: "src/main.ts",
formats: ["es"],
},
},
plugins: [noparse()],
});
これで自分の vite 環境下の typescript のビルドが 9000ms から 330ms になりました。
制約
ESM だと変換の都合で、必ず非同期の loader になります。
import load from "./esm-code.js?noparse"; // export default () => console.log('hello')
const mod = await load();
mod.default(); // => hello
これは ESM のコードを同期的に評価する方法がなく、 import("data:base64,..")
に変換してるので、必ず非同期APIになってしまう、という事情で、 dynamic import と同じように扱う必要があります。また、node 環境下では割と最近のバージョンじゃないと data uri を評価できません。
base64 化のコストもなくはないので、実行コストが掛かります。キャッシュすれば早くなるでしょうが…
その代わりに、外部 chunk 化されるので watch 環境のビルドは早くなるでしょう。
cjs / umd の場合は new Function
で同期的に評価します。
// --- with umd (always sync) ---
import ts from "typescript";
// noparse-umd
import ts from "typescript/lib/typescript.js?noparse-umd";
とはいえ、開発時に使うものを想定してるので、本番環境では eval / new Function を使う関係上、使用を避けたほうがいいでしょう。
普通に noparse ほしいよね
というのを rollup の issue に書いておきました。
Discussion