The plugin's test
Holds craftz() to never overriding the repository. Each case merges what the plugin returns
into a config, the way Vite does. Unset keys are filled in and the repository's own stay.
A value set the same passes, and a value set differently fails, naming each key.
Each config has its own folder as its root, since a site's config follows from the files
there: a Worker config, and an index.html or none.
import { mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { mergeConfig } from "vite-plus";
import { expect, test } from "vite-plus/test";
import { craftz } from "../src/index.ts";
import { BROWSERS } from "../src/pinned.ts";
type Config = Record<string, unknown>;
// A folder holding these files, as a config's root.
function folder(files: Record<string, string> = {}): string {
const dir = mkdtempSync(join(tmpdir(), "craftz-plugin-"));
for (const [file, text] of Object.entries(files)) writeFileSync(join(dir, file), text);
return dir;
}
// The config Vite ends with, without its root.
function resolved(config: Config, root = folder()): Config {
const at = { ...config, root };
const { root: _, ...rest } = mergeConfig(at, craftz().config(at)) as Config;
return rest;
}
const STATIC = `export default defineConfig({
worker: {
name: "example",
},
});
`;
const APP = `export default defineConfig({
worker: {
name: "example",
entrypoint: "src/worker.ts",
},
});
`;
test("an empty config gets what the rules set", () => {
expect(resolved({})).toEqual({
fmt: { proseWrap: "never" },
lint: {
plugins: ["typescript", "unicorn", "import"],
categories: { correctness: "error" },
options: { typeAware: true, typeCheck: true },
},
});
});
test("the repository's own keys stay as they are", () => {
const config = {
fmt: { ignorePatterns: ["fixtures/**"] },
lint: { plugins: ["typescript", "unicorn", "import"], options: { typeAware: true } },
test: { include: ["tests/**/*.test.ts"] },
};
const out = resolved(config);
expect(out["fmt"]).toEqual({ ignorePatterns: ["fixtures/**"], proseWrap: "never" });
expect(out["test"]).toEqual({ include: ["tests/**/*.test.ts"] });
// An array the repository sets isn't joined with the plugin's.
expect((out["lint"] as Config)["plugins"]).toEqual(["typescript", "unicorn", "import"]);
expect((out["lint"] as Config)["options"]).toEqual({ typeAware: true, typeCheck: true });
});
test("a value set differently fails, naming each key", () => {
const config = {
fmt: { proseWrap: "preserve" },
lint: { plugins: ["typescript"], options: { typeCheck: false } },
};
expect(() => craftz().config({ ...config, root: folder() })).toThrow(
[
"craftz() won't override vite.config.ts. Take these out of it:",
' fmt.proseWrap is "preserve", and the rules set "never"',
' lint.plugins is ["typescript"], and the rules set ["typescript","unicorn","import"]',
" lint.options.typeCheck is false, and the rules set true",
].join("\n"),
);
});
test("a static site with an index.html gets the browsers as its build target", () => {
const root = folder({ "cloudflare.config.ts": STATIC, "index.html": "<!doctype html>" });
expect(resolved({}, root)["build"]).toEqual({ target: BROWSERS });
expect(resolved({}, root)["publicDir"]).toBeUndefined();
});
test("a static site without one serves the folder prose writes", () => {
const root = folder({ "cloudflare.config.ts": STATIC });
expect(resolved({}, root)["publicDir"]).toBe(".prose");
expect(resolved({}, root)["build"]).toBeUndefined();
});
test("an app and a folder with no Worker get no site config", () => {
for (const root of [folder({ "cloudflare.config.ts": APP, "index.html": "" }), folder()]) {
const out = resolved({}, root);
expect([out["build"], out["publicDir"]]).toEqual([undefined, undefined]);
}
});
test("a site's config set differently fails too", () => {
const root = folder({ "cloudflare.config.ts": STATIC });
expect(() => craftz().config({ publicDir: "public", root })).toThrow(
' publicDir is "public", and the rules set ".prose"',
);
});