mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-02 17:54:22 +10:00
chore: remove unused UI parts, dependencies and turbo tasks, and share escapeHtml
This commit is contained in:
@@ -62,7 +62,6 @@
|
||||
"better-auth": "1.7.6",
|
||||
"cjk-regex": "^3.5.0",
|
||||
"css-tree": "^3.2.1",
|
||||
"deepmerge-ts": "^8.0.2",
|
||||
"drizzle-orm": "1.0.0-rc.4",
|
||||
"drizzle-zod": "1.0.0-beta.14-a36c63d",
|
||||
"es-toolkit": "^1.52.0",
|
||||
|
||||
@@ -46,7 +46,6 @@
|
||||
"bcrypt",
|
||||
"cjk-regex",
|
||||
"css-tree",
|
||||
"deepmerge-ts",
|
||||
"drizzle-zod",
|
||||
"fast-json-patch",
|
||||
"fast-png",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { escapeHtml } from "@reactive-resume/utils/string";
|
||||
/**
|
||||
* Converts a summary string and optional highlights array into an HTML description.
|
||||
* Summary becomes a <p> tag, highlights become a <ul> list.
|
||||
@@ -32,14 +33,6 @@ export function arrayToHtmlList(items: string[]): string {
|
||||
|
||||
export const BULLET_PATTERN = /^\s*[-–—•*◦‣·]\s+/;
|
||||
|
||||
const escapeHtml = (value: string) =>
|
||||
value
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
|
||||
/**
|
||||
* Converts plain-text lines into escaped HTML: a <ul> when most lines are bullets, otherwise one <p> per line.
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"dependencies": {
|
||||
"@bramus/specificity": "^2.4.2",
|
||||
"@reactive-resume/schema": "workspace:*",
|
||||
"@reactive-resume/utils": "workspace:*",
|
||||
"css-tree": "^3.2.1",
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"wink-porter2-stemmer": "2.0.1",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { CoverLetter, CoverLetterStyle } from "@reactive-resume/schema/cover-letter/data";
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { escapeHtml } from "@reactive-resume/utils/string";
|
||||
|
||||
export function copyCoverLetterStyle(
|
||||
data: ResumeData,
|
||||
@@ -79,14 +80,6 @@ export function detachEmbeddedLetters(data: ResumeData): EmbeddedLetter[] {
|
||||
);
|
||||
}
|
||||
|
||||
const escapeHtml = (text: string) =>
|
||||
text
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
|
||||
export function coverLetterTextToHtml(text: string): string {
|
||||
return text
|
||||
.trim()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import { escapeHtml } from "@reactive-resume/utils/string";
|
||||
|
||||
/**
|
||||
* Suggested edits to a document's text, shared by Check, the assistant and Improve. A proposal replaces one passage
|
||||
@@ -101,9 +102,6 @@ export function splitBlock(html: string) {
|
||||
return match ? { open: match[1] ?? "", inner: match[3] ?? "", close: match[4] ?? "" } : undefined;
|
||||
}
|
||||
|
||||
const escapeHtml = (text: string) =>
|
||||
text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||
|
||||
/**
|
||||
* The block with its text replaced by plain text (a model's rewrite), keeping the tag and its attributes. An empty
|
||||
* field's "block" becomes a paragraph.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Alert, AlertAction, AlertDescription, AlertTitle } from "./alert";
|
||||
import { Alert, AlertDescription, AlertTitle } from "./alert";
|
||||
|
||||
describe("Alert", () => {
|
||||
it("announces only errors", () => {
|
||||
@@ -44,27 +44,4 @@ describe("AlertDescription", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("AlertAction", () => {
|
||||
it("renders children", () => {
|
||||
render(<AlertAction>Action</AlertAction>);
|
||||
expect(screen.getByText("Action")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("applies data-slot='alert-action'", () => {
|
||||
render(<AlertAction>x</AlertAction>);
|
||||
expect(screen.getByText("x")).toHaveAttribute("data-slot", "alert-action");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Alert composition", () => {
|
||||
it("composes all subcomponents", () => {
|
||||
render(
|
||||
<Alert variant="error">
|
||||
<AlertTitle>Title</AlertTitle>
|
||||
<AlertDescription>Body</AlertDescription>
|
||||
<AlertAction>OK</AlertAction>
|
||||
</Alert>,
|
||||
);
|
||||
expect(screen.getByRole("alert")).toHaveTextContent(/TitleBodyOK/);
|
||||
});
|
||||
});
|
||||
describe("Alert composition", () => {});
|
||||
|
||||
@@ -59,8 +59,4 @@ function AlertDescription({ className, ...props }: React.ComponentProps<"div">)
|
||||
);
|
||||
}
|
||||
|
||||
function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return <div data-slot="alert-action" className={cn("absolute inset-e-2 top-2", className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Alert, AlertAction, AlertDescription, AlertTitle, alertVariants };
|
||||
export { Alert, AlertDescription, AlertTitle, alertVariants };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage } from "./avatar";
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
|
||||
|
||||
describe("Avatar", () => {
|
||||
it("renders with data-slot='avatar'", () => {
|
||||
@@ -40,41 +40,6 @@ describe("AvatarFallback", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("AvatarBadge", () => {
|
||||
it("renders as span with data-slot='avatar-badge'", () => {
|
||||
render(<AvatarBadge data-testid="b" />);
|
||||
const badge = screen.getByTestId("b");
|
||||
expect(badge.tagName).toBe("SPAN");
|
||||
expect(badge).toHaveAttribute("data-slot", "avatar-badge");
|
||||
});
|
||||
});
|
||||
|
||||
describe("AvatarGroup", () => {
|
||||
it("renders as div with data-slot='avatar-group'", () => {
|
||||
render(<AvatarGroup data-testid="g" />);
|
||||
const group = screen.getByTestId("g");
|
||||
expect(group.tagName).toBe("DIV");
|
||||
expect(group).toHaveAttribute("data-slot", "avatar-group");
|
||||
});
|
||||
|
||||
it("supports children", () => {
|
||||
render(
|
||||
<AvatarGroup data-testid="g">
|
||||
<Avatar />
|
||||
<Avatar />
|
||||
</AvatarGroup>,
|
||||
);
|
||||
expect(screen.getByTestId("g").children).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AvatarGroupCount", () => {
|
||||
it("renders count children", () => {
|
||||
render(<AvatarGroupCount>+3</AvatarGroupCount>);
|
||||
expect(screen.getByText("+3")).toHaveAttribute("data-slot", "avatar-group-count");
|
||||
});
|
||||
});
|
||||
|
||||
describe("AvatarImage", () => {
|
||||
it("accepts src prop without throwing", () => {
|
||||
render(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type * as React from "react";
|
||||
import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar";
|
||||
import { cn } from "@reactive-resume/utils/style";
|
||||
|
||||
@@ -48,46 +47,4 @@ function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props)
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="avatar-badge"
|
||||
className={cn(
|
||||
"absolute inset-e-0 bottom-0 z-10 inline-flex select-none items-center justify-center rounded-full bg-accent text-on-accent bg-blend-color ring-2 ring-bg",
|
||||
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
||||
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
||||
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="avatar-group"
|
||||
className={cn(
|
||||
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-bg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="avatar-group-count"
|
||||
className={cn(
|
||||
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-sunken text-ink-2 text-sm ring-2 ring-bg group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage };
|
||||
export { Avatar, AvatarFallback, AvatarImage };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ButtonGroup, ButtonGroupSeparator, ButtonGroupText } from "./button-group";
|
||||
import { ButtonGroup } from "./button-group";
|
||||
|
||||
describe("ButtonGroup", () => {
|
||||
it("renders a fieldset with data-slot='button-group'", () => {
|
||||
@@ -25,37 +25,3 @@ describe("ButtonGroup", () => {
|
||||
expect(screen.getByTestId("g")).toHaveClass("my-custom");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ButtonGroupText", () => {
|
||||
it("renders content inside a div by default", () => {
|
||||
render(<ButtonGroupText>Hello</ButtonGroupText>);
|
||||
expect(screen.getByText("Hello").tagName).toBe("DIV");
|
||||
});
|
||||
|
||||
it("merges custom className", () => {
|
||||
render(<ButtonGroupText className="my-class">x</ButtonGroupText>);
|
||||
expect(screen.getByText("x")).toHaveClass("my-class");
|
||||
});
|
||||
|
||||
it("supports custom render function", () => {
|
||||
render(<ButtonGroupText render={(props) => <span {...props} />}>label</ButtonGroupText>);
|
||||
expect(screen.getByText("label").tagName).toBe("SPAN");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ButtonGroupSeparator", () => {
|
||||
it("defaults to vertical orientation", () => {
|
||||
render(<ButtonGroupSeparator data-testid="sep" />);
|
||||
expect(screen.getByTestId("sep")).toHaveAttribute("data-orientation", "vertical");
|
||||
});
|
||||
|
||||
it("supports horizontal orientation", () => {
|
||||
render(<ButtonGroupSeparator data-testid="sep" orientation="horizontal" />);
|
||||
expect(screen.getByTestId("sep")).toHaveAttribute("data-orientation", "horizontal");
|
||||
});
|
||||
|
||||
it("uses data-slot='button-group-separator'", () => {
|
||||
render(<ButtonGroupSeparator data-testid="sep" />);
|
||||
expect(screen.getByTestId("sep")).toHaveAttribute("data-slot", "button-group-separator");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { VariantProps } from "class-variance-authority";
|
||||
import { mergeProps } from "@base-ui/react/merge-props";
|
||||
import { useRender } from "@base-ui/react/use-render";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { Separator } from "@reactive-resume/ui/components/separator";
|
||||
import { cn } from "@reactive-resume/utils/style";
|
||||
|
||||
const buttonGroupVariants = cva(
|
||||
@@ -37,41 +34,4 @@ function ButtonGroup({
|
||||
);
|
||||
}
|
||||
|
||||
function ButtonGroupText({ className, render, ...props }: useRender.ComponentProps<"div">) {
|
||||
return useRender({
|
||||
defaultTagName: "div",
|
||||
props: mergeProps<"div">(
|
||||
{
|
||||
className: cn(
|
||||
"flex items-center gap-2 rounded-md border border-line-2 bg-sunken px-2.5 font-medium text-ink-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
|
||||
className,
|
||||
),
|
||||
},
|
||||
props,
|
||||
),
|
||||
render,
|
||||
state: {
|
||||
slot: "button-group-text",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function ButtonGroupSeparator({
|
||||
className,
|
||||
orientation = "vertical",
|
||||
...props
|
||||
}: React.ComponentProps<typeof Separator>) {
|
||||
return (
|
||||
<Separator
|
||||
data-slot="button-group-separator"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"relative self-stretch bg-line-2 data-horizontal:mx-px data-vertical:my-px data-vertical:h-auto data-horizontal:w-auto",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };
|
||||
export { ButtonGroup, buttonGroupVariants };
|
||||
|
||||
@@ -3,14 +3,7 @@ import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, test } from "vitest";
|
||||
import { createPortal } from "react-dom";
|
||||
import { FormControl, FormItem, FormLabel } from "./form";
|
||||
import {
|
||||
InputGroup,
|
||||
InputGroupAddon,
|
||||
InputGroupButton,
|
||||
InputGroupInput,
|
||||
InputGroupText,
|
||||
InputGroupTextarea,
|
||||
} from "./input-group";
|
||||
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText } from "./input-group";
|
||||
|
||||
function PortaledInput() {
|
||||
return createPortal(<input aria-label="Portaled input" />, document.body);
|
||||
@@ -203,12 +196,3 @@ describe("InputGroupInput", () => {
|
||||
expect(screen.getByTestId("i")).toHaveAttribute("data-slot", "input-group-control");
|
||||
});
|
||||
});
|
||||
|
||||
describe("InputGroupTextarea", () => {
|
||||
it("uses data-slot='input-group-control' and renders textarea", () => {
|
||||
render(<InputGroupTextarea data-testid="t" />);
|
||||
const t = screen.getByTestId("t");
|
||||
expect(t.tagName).toBe("TEXTAREA");
|
||||
expect(t).toHaveAttribute("data-slot", "input-group-control");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { cva } from "class-variance-authority";
|
||||
import { Button } from "@reactive-resume/ui/components/button";
|
||||
import { useFormControl } from "@reactive-resume/ui/components/form";
|
||||
import { Input } from "@reactive-resume/ui/components/input";
|
||||
import { Textarea } from "@reactive-resume/ui/components/textarea";
|
||||
import { cn } from "@reactive-resume/utils/style";
|
||||
|
||||
function InputGroup({
|
||||
@@ -161,31 +160,4 @@ function InputGroupInput({
|
||||
);
|
||||
}
|
||||
|
||||
function InputGroupTextarea({
|
||||
className,
|
||||
id: idProp,
|
||||
"aria-describedby": ariaDescribedByProp,
|
||||
"aria-invalid": ariaInvalidProp,
|
||||
...props
|
||||
}: React.ComponentProps<"textarea">) {
|
||||
const formControl = useFormControl();
|
||||
const controlId = idProp ?? formControl.id;
|
||||
const describedBy = ariaDescribedByProp ?? formControl["aria-describedby"];
|
||||
const invalid = ariaInvalidProp ?? formControl["aria-invalid"];
|
||||
|
||||
return (
|
||||
<Textarea
|
||||
data-slot="input-group-control"
|
||||
id={controlId}
|
||||
aria-describedby={describedBy}
|
||||
aria-invalid={invalid}
|
||||
className={cn(
|
||||
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none focus:shadow-none disabled:bg-transparent aria-invalid:focus:shadow-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea };
|
||||
export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Kbd, KbdGroup } from "./kbd";
|
||||
import { Kbd } from "./kbd";
|
||||
|
||||
describe("Kbd", () => {
|
||||
it("renders as a <kbd> element with data-slot='kbd'", () => {
|
||||
@@ -24,32 +24,3 @@ describe("Kbd", () => {
|
||||
expect(screen.getByTestId("k")).toHaveClass("my-class");
|
||||
});
|
||||
});
|
||||
|
||||
describe("KbdGroup", () => {
|
||||
it("renders as a <kbd> wrapper with data-slot='kbd-group'", () => {
|
||||
render(<KbdGroup data-testid="g">x</KbdGroup>);
|
||||
const g = screen.getByTestId("g");
|
||||
expect(g.tagName).toBe("KBD");
|
||||
expect(g).toHaveAttribute("data-slot", "kbd-group");
|
||||
});
|
||||
|
||||
it("groups multiple Kbd elements", () => {
|
||||
render(
|
||||
<KbdGroup>
|
||||
<Kbd>Ctrl</Kbd>
|
||||
<Kbd>K</Kbd>
|
||||
</KbdGroup>,
|
||||
);
|
||||
expect(screen.getByText("Ctrl")).toBeInTheDocument();
|
||||
expect(screen.getByText("K")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("merges custom className", () => {
|
||||
render(
|
||||
<KbdGroup data-testid="g" className="my-group">
|
||||
x
|
||||
</KbdGroup>,
|
||||
);
|
||||
expect(screen.getByTestId("g")).toHaveClass("my-group");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,8 +13,4 @@ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||
);
|
||||
}
|
||||
|
||||
function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return <kbd data-slot="kbd-group" className={cn("inline-flex items-center gap-1", className)} {...props} />;
|
||||
}
|
||||
|
||||
export { Kbd, KbdGroup };
|
||||
export { Kbd };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger } from "./popover";
|
||||
import { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger } from "./popover";
|
||||
|
||||
describe("Popover", () => {
|
||||
it("trigger element has data-slot='popover-trigger'", () => {
|
||||
@@ -78,20 +78,6 @@ describe("PopoverTitle", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("PopoverDescription", () => {
|
||||
it("uses data-slot='popover-description'", () => {
|
||||
render(
|
||||
<Popover open>
|
||||
<PopoverTrigger>Open</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverDescription>My description</PopoverDescription>
|
||||
</PopoverContent>
|
||||
</Popover>,
|
||||
);
|
||||
expect(screen.getByText("My description")).toHaveAttribute("data-slot", "popover-description");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PopoverContent", () => {
|
||||
it("uses data-slot='popover-content'", () => {
|
||||
render(
|
||||
|
||||
@@ -51,10 +51,4 @@ function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
|
||||
);
|
||||
}
|
||||
|
||||
function PopoverDescription({ className, ...props }: PopoverPrimitive.Description.Props) {
|
||||
return (
|
||||
<PopoverPrimitive.Description data-slot="popover-description" className={cn("text-ink-2", className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export { Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger };
|
||||
export { Popover, PopoverContent, PopoverHeader, PopoverTitle, PopoverTrigger };
|
||||
|
||||
@@ -67,10 +67,6 @@ export function isLocale(value: unknown): value is Locale {
|
||||
return localeSchema.safeParse(value).success;
|
||||
}
|
||||
|
||||
export function isCJKLocale(locale: Locale): boolean {
|
||||
return locale === "zh-CN" || locale === "zh-TW" || locale === "ja-JP" || locale === "ko-KR";
|
||||
}
|
||||
|
||||
// A writing system that needs a dedicated fallback font in the PDF renderer,
|
||||
// because react-pdf (unlike a browser) has no automatic system-font fallback:
|
||||
// a glyph only renders if a registered font contains it. We pick the matching
|
||||
|
||||
@@ -66,6 +66,15 @@ export function generateRandomName() {
|
||||
* @param html - The HTML string to strip.
|
||||
* @returns The text content without HTML tags.
|
||||
*/
|
||||
/** Text safe to place inside HTML: markup characters and both quote marks become entities. */
|
||||
export const escapeHtml = (text: string) =>
|
||||
text
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
|
||||
export function stripHtml(html: string | undefined): string {
|
||||
if (!html) return "";
|
||||
return html.replace(/<[^>]*>/g, "").trim();
|
||||
|
||||
Generated
+3
-9
@@ -386,9 +386,6 @@ importers:
|
||||
css-tree:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
deepmerge-ts:
|
||||
specifier: ^8.0.2
|
||||
version: 8.0.2
|
||||
drizzle-orm:
|
||||
specifier: 1.0.0-rc.4
|
||||
version: 1.0.0-rc.4(@types/pg@8.23.1)(pg@8.23.0)(zod@4.6.5)
|
||||
@@ -1337,6 +1334,9 @@ importers:
|
||||
'@reactive-resume/schema':
|
||||
specifier: workspace:*
|
||||
version: link:../schema
|
||||
'@reactive-resume/utils':
|
||||
specifier: workspace:*
|
||||
version: link:../utils
|
||||
css-tree:
|
||||
specifier: ^3.2.1
|
||||
version: 3.2.1
|
||||
@@ -5883,10 +5883,6 @@ packages:
|
||||
babel-plugin-macros:
|
||||
optional: true
|
||||
|
||||
deepmerge-ts@8.0.2:
|
||||
resolution: {integrity: sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
|
||||
deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -13085,8 +13081,6 @@ snapshots:
|
||||
optionalDependencies:
|
||||
babel-plugin-macros: 3.1.0
|
||||
|
||||
deepmerge-ts@8.0.2: {}
|
||||
|
||||
deepmerge@4.3.1: {}
|
||||
|
||||
default-browser-id@5.0.1: {}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { afterAll, beforeAll, expect, it } from "vitest";
|
||||
|
||||
const root = fileURLToPath(new URL("../../", import.meta.url));
|
||||
const turbo = join(root, "node_modules", "turbo", "bin", "turbo");
|
||||
const tasks = ["build", "check", "typecheck", "test", "test:coverage", "test:ci", "test:agent"];
|
||||
const tasks = ["build", "typecheck", "test", "test:coverage", "test:ci", "test:agent"];
|
||||
type DryTask = { taskId: string; task: string; hash: string; command: string; dependencies: string[] };
|
||||
let directory: string;
|
||||
let baseline: Map<string, DryTask>;
|
||||
|
||||
@@ -119,9 +119,6 @@
|
||||
"outputs": ["dist/**", ".vercel/**"],
|
||||
"dependsOn": ["transit"]
|
||||
},
|
||||
"check": {
|
||||
"dependsOn": ["transit"]
|
||||
},
|
||||
"typecheck": {
|
||||
"dependsOn": ["transit"]
|
||||
},
|
||||
@@ -134,9 +131,6 @@
|
||||
"test:ci": {
|
||||
"dependsOn": ["transit"]
|
||||
},
|
||||
"test:e2e": {
|
||||
"cache": false
|
||||
},
|
||||
"test:agent": {
|
||||
"dependsOn": ["transit"]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user