Files
cloudflare__vinext/tests/next-config.test.ts
T
Nathan Nguyen 032a2afb41 fix: preserve next.config plugin aliases for next-intl (#342)
* fix: preserve next.config plugin aliases

* refactor: probe wrapped webpack config once

* fix: tighten next.config alias warnings
2026-03-08 15:30:05 +00:00

228 lines
6.9 KiB
TypeScript

import { describe, it, expect, afterEach, vi } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { loadNextConfig, resolveNextConfig } from "../packages/vinext/src/config/next-config.js";
import { PHASE_PRODUCTION_BUILD, PHASE_DEVELOPMENT_SERVER } from "../packages/vinext/src/shims/constants.js";
function makeTempDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), "vinext-config-test-"));
}
describe("loadNextConfig phase argument", () => {
let tmpDir: string;
afterEach(() => {
vi.restoreAllMocks();
if (tmpDir) {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
});
it("passes phase-production-build to function-form config when phase is specified", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default (phase) => ({ env: { RECEIVED_PHASE: phase } });\n`,
);
const config = await loadNextConfig(tmpDir, PHASE_PRODUCTION_BUILD);
expect(config?.env?.RECEIVED_PHASE).toBe(PHASE_PRODUCTION_BUILD);
});
it("defaults to phase-development-server when no phase is provided", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default (phase) => ({ env: { RECEIVED_PHASE: phase } });\n`,
);
const config = await loadNextConfig(tmpDir);
expect(config?.env?.RECEIVED_PHASE).toBe(PHASE_DEVELOPMENT_SERVER);
});
it("ignores phase for object-form config", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default { env: { STATIC: "yes" } };\n`,
);
const config = await loadNextConfig(tmpDir, PHASE_PRODUCTION_BUILD);
expect(config?.env?.STATIC).toBe("yes");
});
});
describe("resolveNextConfig alias extraction", () => {
let tmpDir: string;
afterEach(() => {
if (tmpDir) {
fs.rmSync(tmpDir, { recursive: true, force: true });
}
});
it("captures webpack resolve.alias from wrapped config plugins", async () => {
tmpDir = makeTempDir();
fs.mkdirSync(path.join(tmpDir, "node_modules", "fake-plugin"), {
recursive: true,
});
fs.writeFileSync(
path.join(tmpDir, "node_modules", "fake-plugin", "index.js"),
`module.exports = function fakePlugin() {
return function withPlugin(nextConfig = {}) {
return Object.assign({}, nextConfig, {
webpack(config) {
config.resolve = config.resolve || {};
config.resolve.alias = config.resolve.alias || {};
config.resolve.alias["wrapped/config"] = "./config/request.ts";
return typeof nextConfig.webpack === "function"
? nextConfig.webpack(config)
: config;
}
});
};
};`,
);
fs.writeFileSync(
path.join(tmpDir, "node_modules", "fake-plugin", "package.json"),
JSON.stringify({ name: "fake-plugin", version: "1.0.0", main: "index.js" }),
);
fs.writeFileSync(
path.join(tmpDir, "next.config.js"),
`const withPlugin = require("fake-plugin")();
module.exports = withPlugin({ basePath: "/wrapped" });`,
);
const rawConfig = await loadNextConfig(tmpDir);
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(config.basePath).toBe("/wrapped");
expect(config.aliases["wrapped/config"]).toBe(
path.join(tmpDir, "config", "request.ts"),
);
});
it("captures turbopack aliases from wrapped config plugins", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default {
experimental: {
turbo: {
resolveAlias: {
"wrapped/config": "./turbo/request.ts"
}
}
}
};`,
);
const rawConfig = await loadNextConfig(tmpDir);
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(config.aliases["wrapped/config"]).toBe(
path.join(tmpDir, "turbo", "request.ts"),
);
});
it("captures top-level turbopack aliases", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default {
turbopack: {
resolveAlias: {
"wrapped/config": "./turbopack/request.ts"
}
}
};`,
);
const rawConfig = await loadNextConfig(tmpDir);
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(config.aliases["wrapped/config"]).toBe(
path.join(tmpDir, "turbopack", "request.ts"),
);
});
it("does not attribute turbopack aliases to webpack support warnings", async () => {
tmpDir = makeTempDir();
const consoleWarn = vi.spyOn(console, "warn").mockImplementation(() => {});
const rawConfig = {
turbopack: {
resolveAlias: {
"wrapped/config": "./turbopack/request.ts",
},
},
webpack: (webpackConfig: any) => webpackConfig,
};
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(config.aliases["wrapped/config"]).toBe(
path.join(tmpDir, "turbopack", "request.ts"),
);
expect(consoleWarn).toHaveBeenCalledWith(
'[vinext] next.config option "webpack" is not yet supported and will be ignored',
);
});
it("keeps unrelated config resolution unchanged when no aliases exist", async () => {
tmpDir = makeTempDir();
fs.writeFileSync(
path.join(tmpDir, "next.config.mjs"),
`export default {
basePath: "/docs",
env: { FEATURE_FLAG: "on" }
};`,
);
const rawConfig = await loadNextConfig(tmpDir);
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(config.basePath).toBe("/docs");
expect(config.env.FEATURE_FLAG).toBe("on");
expect(config.aliases).toEqual({});
});
it("extracts aliases and mdx from a single async webpack probe", async () => {
tmpDir = makeTempDir();
let invocations = 0;
const fakeRemarkPlugin = () => {};
const rawConfig = {
webpack: async (webpackConfig: any) => {
invocations++;
webpackConfig.resolve = webpackConfig.resolve || {};
webpackConfig.resolve.alias = webpackConfig.resolve.alias || {};
webpackConfig.resolve.alias["wrapped/config"] = "./config/request.ts";
webpackConfig.module = webpackConfig.module || { rules: [] };
webpackConfig.module.rules.push({
test: /\.mdx$/,
use: [
{
loader: "@next/mdx/mdx-js-loader",
options: {
remarkPlugins: [fakeRemarkPlugin],
},
},
],
});
return webpackConfig;
},
};
const config = await resolveNextConfig(rawConfig, tmpDir);
expect(invocations).toBe(1);
expect(config.aliases["wrapped/config"]).toBe(
path.join(tmpDir, "config", "request.ts"),
);
expect(config.mdx?.remarkPlugins).toEqual([fakeRemarkPlugin]);
});
});