mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-23 23:02:17 +10:00
Finding 5: Introduce V4Url + V4Section<T> type aliases in reactive-resume-v4-json.tsx; collapse
310-line V4ResumeData type (13x 5-field section header, 11x {label,href}) into typed aliases.
Inferred shape is structurally identical.
Finding 6: Remove 9 single-caller clamp/pxToPt wrappers; replace compile-time constants
(rotation=0, sidebarWidth=35, shadowWidth=0, gapX=4, gapY=6) with literals; inline dynamic
calls (clamp(x, 32, 512) etc.) at the two body/heading typography call sites.
Finding 7: Convert three stateless single-method importer classes to plain functions.
Extract shared rethrowAsImportError() to error.ts, replacing triplicated ZodError catch blocks.
Update web import dialog + all in-package test files to use function API.
Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
|
import { ZodError } from "zod";
|
|
import { resumeDataSchema, sectionTypeSchema } from "@reactive-resume/schema/resume/data";
|
|
import { rethrowAsImportError } from "./error";
|
|
|
|
const BUILT_IN_LAYOUT_SECTION_IDS = sectionTypeSchema.options.filter((section) => section !== "cover-letter");
|
|
|
|
function normalizeBuiltInSectionsInLayout(data: ResumeData): ResumeData {
|
|
const pages = data.metadata.layout.pages;
|
|
|
|
// Some exported resumes can arrive without any persisted layout pages.
|
|
if (pages.length === 0) {
|
|
return {
|
|
...data,
|
|
metadata: {
|
|
...data.metadata,
|
|
layout: {
|
|
...data.metadata.layout,
|
|
pages: [
|
|
{
|
|
fullWidth: false,
|
|
main: [...BUILT_IN_LAYOUT_SECTION_IDS],
|
|
sidebar: [],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
const existingSectionIds = new Set<string>();
|
|
|
|
// Preserve the imported layout and only compute which built-in IDs are missing entirely.
|
|
for (const page of pages) {
|
|
for (const sectionId of page.main) existingSectionIds.add(sectionId);
|
|
for (const sectionId of page.sidebar) existingSectionIds.add(sectionId);
|
|
}
|
|
|
|
const missingSectionIds = BUILT_IN_LAYOUT_SECTION_IDS.filter((sectionId) => !existingSectionIds.has(sectionId));
|
|
|
|
if (missingSectionIds.length === 0) return data;
|
|
|
|
const [firstPage, ...restPages] = pages;
|
|
if (!firstPage) return data;
|
|
|
|
return {
|
|
...data,
|
|
metadata: {
|
|
...data.metadata,
|
|
layout: {
|
|
...data.metadata.layout,
|
|
pages: [
|
|
{
|
|
fullWidth: firstPage.fullWidth ?? false,
|
|
sidebar: firstPage.sidebar ?? [],
|
|
// Recover missing built-in sections without reordering the imported layout.
|
|
main: [...firstPage.main, ...missingSectionIds],
|
|
},
|
|
...restPages,
|
|
],
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
// ponytail: stateless single-method class → plain function
|
|
export function parseReactiveResumeJSON(json: string): ResumeData {
|
|
try {
|
|
const parsed = resumeDataSchema.parse(JSON.parse(json));
|
|
return resumeDataSchema.parse(normalizeBuiltInSectionsInLayout(parsed));
|
|
} catch (error) {
|
|
if (error instanceof ZodError) rethrowAsImportError(error);
|
|
throw error;
|
|
}
|
|
}
|