Initial Commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export type { ViteAppConfigOptions } from "./vite-app-config.js";
|
||||
export { createViteAppConfig } from "./vite-app-config.js";
|
||||
@@ -0,0 +1,54 @@
|
||||
# vite-app-config
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`createViteAppConfig` builds a Vite `UserConfig` tailored for consumer applications rather than library packages. It defaults to hashed asset filenames so long-term caching works out of the box, targets `es2022` to match the project's TypeScript output, and configures `envPrefix` so the standard `VITE_*` (or any caller-supplied prefix) is the only env surface exposed to client bundles.
|
||||
|
||||
## Features
|
||||
|
||||
- Hashed asset / chunk / entry filenames under `assets/[name]-[hash][extname]`.
|
||||
- `target: 'es2022'` default to match the `tsconfig.base.json` setting.
|
||||
- `envPrefix` default `['VITE_']` with full opt-in override.
|
||||
- Zod-validated option boundary; bad input throws `TestViteError(TEST_VITE.INVALID_OPTIONS)`.
|
||||
- OTel counter `test_vite_config_built_total{kind="vite_app"}` incremented per invocation.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
// vite.config.ts
|
||||
import { createViteAppConfig } from "@suaveplan/test-vite/vite-app-config";
|
||||
|
||||
export default createViteAppConfig({ root: __dirname });
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Custom env prefix + plugins:
|
||||
|
||||
```ts
|
||||
import react from "@vitejs/plugin-react";
|
||||
import { createViteAppConfig } from "@suaveplan/test-vite/vite-app-config";
|
||||
|
||||
export default createViteAppConfig({
|
||||
root: __dirname,
|
||||
outDir: "dist",
|
||||
target: "es2023",
|
||||
envPrefix: ["APP_", "PUBLIC_"],
|
||||
sourcemap: true,
|
||||
plugins: [react()],
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `createViteAppConfig(opts: ViteAppConfigOptions): UserConfig`.
|
||||
- `ViteAppConfigOptions` — `{ root, outDir?, target?, envPrefix?, sourcemap?, plugins? }`.
|
||||
- Throws `TestViteError` with code `TEST_VITE.INVALID_OPTIONS` on Zod failure.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The factory normalises `envPrefix` to a string array even when only one prefix is supplied so downstream tooling can iterate uniformly. Asset hashing uses the standard Rollup placeholders; consumers that need a different layout can spread the returned config and override `build.rollupOptions.output`. The factory does not register a framework plugin — pass `react()`, `vue()`, or another framework plugin through the `plugins` option so the factory stays framework-agnostic and you keep one source of truth for plugin order. The Zod boundary rejects unknown options strictly so typos surface immediately at build time rather than failing silently inside Vite.
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { TEST_VITE_CODES, TestViteError } from "../error-codes/error-codes.js";
|
||||
import { createViteAppConfig } from "./vite-app-config.js";
|
||||
|
||||
describe("createViteAppConfig", () => {
|
||||
it("returns app config with hashed asset filenames", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const config = createViteAppConfig({
|
||||
root: ".",
|
||||
envPrefix: ["APP_"],
|
||||
});
|
||||
const output = (config.build?.rollupOptions?.output ?? {}) as {
|
||||
assetFileNames?: string;
|
||||
};
|
||||
expect(output.assetFileNames).toBe("assets/[name]-[hash][extname]");
|
||||
expect(config.envPrefix).toEqual(["APP_"]);
|
||||
});
|
||||
|
||||
it("defaults envPrefix to VITE_ and target to es2022", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
const config = createViteAppConfig({ root: "." });
|
||||
expect(config.envPrefix).toEqual(["VITE_"]);
|
||||
expect(config.build?.target).toBe("es2022");
|
||||
});
|
||||
|
||||
it("throws INVALID_OPTIONS on non-string root", { timeout: 5000 }, () => {
|
||||
let captured: unknown;
|
||||
try {
|
||||
createViteAppConfig({
|
||||
root: 123 as unknown as string,
|
||||
});
|
||||
} catch (err) {
|
||||
captured = err;
|
||||
}
|
||||
expect(captured).toBeInstanceOf(TestViteError);
|
||||
expect((captured as TestViteError).code).toBe(
|
||||
TEST_VITE_CODES.INVALID_OPTIONS
|
||||
);
|
||||
});
|
||||
|
||||
it("honours custom outDir and sourcemap", { timeout: 5000 }, () => {
|
||||
const config = createViteAppConfig({
|
||||
root: ".",
|
||||
outDir: "build",
|
||||
sourcemap: false,
|
||||
plugins: [],
|
||||
});
|
||||
expect(config.build?.outDir).toBe("build");
|
||||
expect(config.build?.sourcemap).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { Plugin, UserConfig } from "vite";
|
||||
import z from "zod";
|
||||
import { TEST_VITE_CODES, TestViteError } from "../error-codes/error-codes.js";
|
||||
import { recordConfigBuilt } from "../telemetry/telemetry.js";
|
||||
|
||||
const ViteAppConfigOptionsSchema = z
|
||||
.object({
|
||||
root: z.string(),
|
||||
outDir: z.string().default("dist"),
|
||||
target: z.string().default("es2022"),
|
||||
envPrefix: z.array(z.string().min(1)).default(["VITE_"]),
|
||||
sourcemap: z.boolean().default(true),
|
||||
plugins: z.array(z.custom<Plugin>()).default([]),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export type ViteAppConfigOptions = z.input<typeof ViteAppConfigOptionsSchema>;
|
||||
|
||||
/**
|
||||
* Build a Vite `UserConfig` tailored for a consumer application.
|
||||
*
|
||||
* - Hashed asset filenames under `assets/[name]-[hash][extname]`.
|
||||
* - `target` defaults to `es2022`.
|
||||
* - `envPrefix` defaults to `['VITE_']`.
|
||||
*
|
||||
* @throws {TestViteError} with `TEST_VITE.INVALID_OPTIONS` on bad input.
|
||||
*/
|
||||
export function createViteAppConfig(opts: ViteAppConfigOptions): UserConfig {
|
||||
const parsed = ViteAppConfigOptionsSchema.safeParse(opts);
|
||||
if (!parsed.success) {
|
||||
throw new TestViteError(
|
||||
TEST_VITE_CODES.INVALID_OPTIONS,
|
||||
"createViteAppConfig: invalid options",
|
||||
{ details: { issues: parsed.error.issues } }
|
||||
);
|
||||
}
|
||||
const o = parsed.data;
|
||||
const config: UserConfig = {
|
||||
root: o.root,
|
||||
envPrefix: o.envPrefix,
|
||||
plugins: [...o.plugins],
|
||||
build: {
|
||||
outDir: o.outDir,
|
||||
sourcemap: o.sourcemap,
|
||||
target: o.target,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
assetFileNames: "assets/[name]-[hash][extname]",
|
||||
chunkFileNames: "assets/[name]-[hash].js",
|
||||
entryFileNames: "assets/[name]-[hash].js",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
recordConfigBuilt("vite_app");
|
||||
return config;
|
||||
}
|
||||
Reference in New Issue
Block a user