mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
032a2afb41
* fix: preserve next.config plugin aliases * refactor: probe wrapped webpack config once * fix: tighten next.config alias warnings
228 lines
6.9 KiB
TypeScript
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]);
|
|
});
|
|
});
|