import assert from "node:assert/strict"; import { existsSync } from "node:fs"; import { mkdtemp } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import test from "node:test"; import { build } from "esbuild"; const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); function resolveNextAlias(importPath) { const basePath = path.join(projectRoot, importPath.slice(2)); const candidates = [ basePath, `${basePath}.ts`, `${basePath}.tsx`, `${basePath}.js`, path.join(basePath, "index.ts"), path.join(basePath, "index.tsx"), path.join(basePath, "index.js"), ]; return candidates.find((candidate) => existsSync(candidate)) ?? basePath; } async function loadValidator() { const outDir = await mkdtemp(path.join(tmpdir(), "layout-validator-test-")); const outfile = path.join(outDir, "validator.mjs"); await build({ entryPoints: [path.join(projectRoot, "lib", "validate-layout-code.ts")], outfile, bundle: true, format: "esm", platform: "node", sourcemap: false, logLevel: "silent", plugins: [ { name: "next-alias", setup(builder) { builder.onResolve({ filter: /^@\// }, (args) => ({ path: resolveNextAlias(args.path), })); }, }, ], }); return import(pathToFileURL(outfile).href); } const validatorPromise = loadValidator(); const validCode = ` import * as z from "zod"; const Schema = z.object({ title: z.string().max(40).default("Hello"), }); const layoutId = "valid-layout"; const layoutName = "Valid Layout"; const layoutDescription = "A valid custom layout."; const dynamicSlideLayout = ({ data }: { data: Partial> }) => { return
{data.title}
; }; export { Schema, layoutId, layoutName, layoutDescription, dynamicSlideLayout }; `; test("accepts valid TSX layout code", async () => { const { validateLayoutCode } = await validatorPromise; const result = validateLayoutCode(validCode); assert.equal(result.layoutId, "valid-layout"); assert.equal(result.layoutName, "Valid Layout"); assert.equal(result.layoutDescription, "A valid custom layout."); assert.equal(result.layout_code.includes("```"), false); assert.equal(typeof result.schemaJSON, "object"); }); test("rejects syntax errors with a source location", async () => { const { LayoutCodeValidationError, validateLayoutCode } = await validatorPromise; assert.throws( () => validateLayoutCode(`${validCode}\nconst extra = );`), (error) => error instanceof LayoutCodeValidationError && error.line !== undefined && error.column !== undefined && /Unexpected token|Expression expected/.test(error.message) ); }); test("rejects malformed JSX tags", async () => { const { LayoutCodeValidationError, validateLayoutCode } = await validatorPromise; assert.throws( () => validateLayoutCode( validCode.replace("
{data.title}
", "
") ), (error) => error instanceof LayoutCodeValidationError && /Expected corresponding JSX closing tag/.test(error.message) ); }); test("rejects missing Schema", async () => { const { LayoutCodeValidationError, validateLayoutCode } = await validatorPromise; const code = validCode .replace("const Schema = z.object", "const NotSchema = z.object") .replace("Schema, ", ""); assert.throws( () => validateLayoutCode(code), (error) => error instanceof LayoutCodeValidationError && error.message === "Layout code must declare Schema" ); }); test("rejects missing dynamicSlideLayout", async () => { const { LayoutCodeValidationError, validateLayoutCode } = await validatorPromise; const code = validCode.replaceAll("dynamicSlideLayout", "StaticSlideLayout"); assert.throws( () => validateLayoutCode(code), (error) => error instanceof LayoutCodeValidationError && error.message === "Layout code must declare dynamicSlideLayout" ); }); test("rejects missing or computed layout metadata", async () => { const { LayoutCodeValidationError, validateLayoutCode } = await validatorPromise; const missingName = validCode .replace('const layoutName = "Valid Layout";', "") .replace("layoutName, ", ""); const computedName = validCode.replace( 'const layoutName = "Valid Layout";', 'const layoutName = ["Valid", "Layout"].join(" ");' ); assert.throws( () => validateLayoutCode(missingName), (error) => error instanceof LayoutCodeValidationError && error.message === "Layout code must declare layoutName" ); assert.throws( () => validateLayoutCode(computedName), (error) => error instanceof LayoutCodeValidationError && error.message === "Layout metadata layoutName must be a non-empty string literal" ); }); test("strips fenced code before validation", async () => { const { validateLayoutCode } = await validatorPromise; const result = validateLayoutCode(`\`\`\`tsx\n${validCode}\n\`\`\``); assert.equal(result.layoutId, "valid-layout"); assert.equal(result.layout_code.startsWith("```"), false); assert.equal(result.layout_code.endsWith("```"), false); });