mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-13 08:13:49 +10:00
refactor(v4.0.0-alpha): beginning of a new era
This commit is contained in:
30
libs/parser/.eslintrc.json
Normal file
30
libs/parser/.eslintrc.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.json"],
|
||||
"parser": "jsonc-eslint-parser",
|
||||
"rules": {
|
||||
"@nx/dependency-checks": [
|
||||
"error",
|
||||
{
|
||||
"ignoredFiles": ["{projectRoot}/vite.config.{js,ts,mjs,mts}"]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
29
libs/parser/.swcrc
Normal file
29
libs/parser/.swcrc
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"jsc": {
|
||||
"target": "es2017",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true,
|
||||
"dynamicImport": true
|
||||
},
|
||||
"transform": {
|
||||
"decoratorMetadata": true,
|
||||
"legacyDecorator": true
|
||||
},
|
||||
"keepClassNames": true,
|
||||
"externalHelpers": true,
|
||||
"loose": true
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
".*\\.spec.tsx?$",
|
||||
".*\\.test.tsx?$",
|
||||
"./src/jest-setup.ts$",
|
||||
"./**/jest-setup.ts$",
|
||||
".*.js$"
|
||||
]
|
||||
}
|
||||
20
libs/parser/package.json
Normal file
20
libs/parser/package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@reactive-resume/parser",
|
||||
"version": "0.0.3",
|
||||
"private": false,
|
||||
"type": "commonjs",
|
||||
"main": "./src/index.js",
|
||||
"typings": "./src/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"@swc/helpers": "~0.5.2",
|
||||
"@reactive-resume/schema": "*",
|
||||
"nestjs-zod": "^3.0.0",
|
||||
"zod": "^3.22.4",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@reactive-resume/utils": "*",
|
||||
"jszip": "^3.10.1"
|
||||
}
|
||||
}
|
||||
34
libs/parser/project.json
Normal file
34
libs/parser/project.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "parser",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/parser/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/js:swc",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/parser",
|
||||
"main": "libs/parser/src/index.ts",
|
||||
"tsConfig": "libs/parser/tsconfig.lib.json",
|
||||
"assets": ["libs/parser/*.md"]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/parser/**/*.ts", "libs/parser/package.json"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/vite:test",
|
||||
"outputs": ["{options.reportsDirectory}"],
|
||||
"options": {
|
||||
"passWithNoTests": true,
|
||||
"reportsDirectory": "../../coverage/libs/parser"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["frontend"]
|
||||
}
|
||||
4
libs/parser/src/index.ts
Normal file
4
libs/parser/src/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./json-resume";
|
||||
export * from "./linkedin";
|
||||
export * from "./reactive-resume";
|
||||
export * from "./reactive-resume-v3";
|
||||
13
libs/parser/src/interfaces/parser.ts
Normal file
13
libs/parser/src/interfaces/parser.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { ResumeData } from "@reactive-resume/schema";
|
||||
import { ZodDto } from "nestjs-zod/dto";
|
||||
import { Schema } from "zod";
|
||||
|
||||
export interface Parser<Data = unknown, T = ZodDto, Result = ResumeData> {
|
||||
schema?: Schema;
|
||||
|
||||
readFile(file: File): Promise<Data>;
|
||||
|
||||
validate(data: Data): T | Promise<T>;
|
||||
|
||||
convert(data: T): Result | Promise<Result>;
|
||||
}
|
||||
224
libs/parser/src/json-resume/index.ts
Normal file
224
libs/parser/src/json-resume/index.ts
Normal file
@ -0,0 +1,224 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import {
|
||||
defaultAward,
|
||||
defaultCertification,
|
||||
defaultEducation,
|
||||
defaultExperience,
|
||||
defaultInterest,
|
||||
defaultLanguage,
|
||||
defaultProfile,
|
||||
defaultPublication,
|
||||
defaultReference,
|
||||
defaultResumeData,
|
||||
defaultSkill,
|
||||
defaultVolunteer,
|
||||
ResumeData,
|
||||
} from "@reactive-resume/schema";
|
||||
import { Json } from "@reactive-resume/utils";
|
||||
import { Schema } from "zod";
|
||||
|
||||
import { Parser } from "../interfaces/parser";
|
||||
import { JsonResume, jsonResumeSchema } from "./schema";
|
||||
|
||||
export * from "./schema";
|
||||
|
||||
export class JsonResumeParser implements Parser<Json, JsonResume> {
|
||||
schema: Schema;
|
||||
|
||||
constructor() {
|
||||
this.schema = jsonResumeSchema;
|
||||
}
|
||||
|
||||
readFile(file: File): Promise<Json> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result as string) as Json;
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(new Error("Failed to parse JSON"));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = () => {
|
||||
reject(new Error("Failed to read the file"));
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
validate(data: Json) {
|
||||
return this.schema.parse(data) as JsonResume;
|
||||
}
|
||||
|
||||
convert(data: JsonResume) {
|
||||
const result = JSON.parse(JSON.stringify(defaultResumeData)) as ResumeData;
|
||||
|
||||
// Basics
|
||||
result.basics.name = data.basics?.name ?? "";
|
||||
result.basics.headline = data.basics?.label ?? "";
|
||||
result.basics.picture.url = data.basics?.image ?? "";
|
||||
result.basics.email = data.basics?.email ?? "";
|
||||
result.basics.phone = data.basics?.phone ?? "";
|
||||
result.basics.location = data.basics?.location?.address ?? "";
|
||||
result.basics.url.href = data.basics?.url ?? "";
|
||||
result.sections.summary.content = data.basics?.summary ?? "";
|
||||
|
||||
// Profiles
|
||||
if (data.basics?.profiles) {
|
||||
for (const profile of data.basics.profiles) {
|
||||
result.sections.profiles.items.push({
|
||||
...defaultProfile,
|
||||
id: createId(),
|
||||
icon: profile.network?.toLocaleLowerCase() ?? "",
|
||||
network: profile.network ?? "",
|
||||
username: profile.username ?? "",
|
||||
url: { ...defaultProfile.url, href: profile.url ?? "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Work
|
||||
if (data.work) {
|
||||
for (const work of data.work) {
|
||||
result.sections.experience.items.push({
|
||||
...defaultExperience,
|
||||
id: createId(),
|
||||
company: work.name ?? "",
|
||||
position: work.position ?? "",
|
||||
summary: work.summary ?? "",
|
||||
date: `${work.startDate} - ${work.endDate}`,
|
||||
url: { ...defaultExperience.url, href: work.url ?? "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Volunteer
|
||||
if (data.volunteer) {
|
||||
for (const volunteer of data.volunteer) {
|
||||
result.sections.volunteer.items.push({
|
||||
...defaultVolunteer,
|
||||
id: createId(),
|
||||
organization: volunteer.organization ?? "",
|
||||
date: `${volunteer.startDate} - ${volunteer.endDate}`,
|
||||
position: volunteer.position ?? "",
|
||||
summary: volunteer.summary ?? "",
|
||||
url: { ...defaultVolunteer.url, href: volunteer.url ?? "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Education
|
||||
if (data.education) {
|
||||
for (const education of data.education) {
|
||||
result.sections.education.items.push({
|
||||
...defaultEducation,
|
||||
id: createId(),
|
||||
institution: education.institution ?? "",
|
||||
studyType: education.studyType ?? "",
|
||||
area: education.area ?? "",
|
||||
score: education.score ?? "",
|
||||
date: `${education.startDate} - ${education.endDate}`,
|
||||
url: { ...defaultEducation.url, href: education.url ?? "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Awards
|
||||
if (data.awards) {
|
||||
for (const award of data.awards) {
|
||||
result.sections.awards.items.push({
|
||||
...defaultAward,
|
||||
id: createId(),
|
||||
title: award.title ?? "",
|
||||
date: award.date ?? "",
|
||||
awarder: award.awarder ?? "",
|
||||
summary: award.summary ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Certificates
|
||||
if (data.certificates) {
|
||||
for (const certificate of data.certificates) {
|
||||
result.sections.certifications.items.push({
|
||||
...defaultCertification,
|
||||
id: createId(),
|
||||
name: certificate.title ?? "",
|
||||
date: certificate.date ?? "",
|
||||
issuer: certificate.issuer ?? "",
|
||||
summary: certificate.summary ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Publications
|
||||
if (data.publications) {
|
||||
for (const publication of data.publications) {
|
||||
result.sections.publications.items.push({
|
||||
...defaultPublication,
|
||||
id: createId(),
|
||||
name: publication.name ?? "",
|
||||
publisher: publication.publisher ?? "",
|
||||
summary: publication.summary ?? "",
|
||||
date: publication.releaseDate ?? "",
|
||||
url: { ...defaultPublication.url, href: publication.url ?? "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Skills
|
||||
if (data.skills) {
|
||||
for (const skill of data.skills) {
|
||||
result.sections.skills.items.push({
|
||||
...defaultSkill,
|
||||
id: createId(),
|
||||
name: skill.name ?? "",
|
||||
description: skill.level ?? "",
|
||||
keywords: skill.keywords ?? [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Languages
|
||||
if (data.languages) {
|
||||
for (const language of data.languages) {
|
||||
result.sections.languages.items.push({
|
||||
...defaultLanguage,
|
||||
id: createId(),
|
||||
name: language.language ?? "",
|
||||
fluency: language.fluency ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Interests
|
||||
if (data.interests) {
|
||||
for (const interest of data.interests) {
|
||||
result.sections.interests.items.push({
|
||||
...defaultInterest,
|
||||
id: createId(),
|
||||
name: interest.name ?? "",
|
||||
keywords: interest.keywords ?? [],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// References
|
||||
if (data.references) {
|
||||
for (const reference of data.references) {
|
||||
result.sections.references.items.push({
|
||||
...defaultReference,
|
||||
id: createId(),
|
||||
name: reference.name ?? "",
|
||||
summary: reference.reference ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
126
libs/parser/src/json-resume/schema.ts
Normal file
126
libs/parser/src/json-resume/schema.ts
Normal file
@ -0,0 +1,126 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const urlSchema = z.literal("").or(z.string().url()).optional();
|
||||
|
||||
const iso8601 = z
|
||||
.string()
|
||||
.regex(
|
||||
/^([1-2][0-9]{3}-[0-1][0-9]-[0-3][0-9]|[1-2][0-9]{3}-[0-1][0-9]|[1-2][0-9]{3})$/,
|
||||
"ISO8601 Date Format",
|
||||
);
|
||||
|
||||
const locationSchema = z.object({
|
||||
address: z.string().optional(),
|
||||
postalCode: z.string().optional(),
|
||||
city: z.string().optional(),
|
||||
countryCode: z.string().optional(),
|
||||
region: z.string().optional(),
|
||||
});
|
||||
|
||||
const profileSchema = z.object({
|
||||
network: z.string().optional(),
|
||||
username: z.string().optional(),
|
||||
url: urlSchema,
|
||||
});
|
||||
|
||||
const basicsSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
label: z.string().optional(),
|
||||
image: z.literal("").or(z.string().url()).optional(),
|
||||
email: z.literal("").or(z.string().email()).optional(),
|
||||
phone: z.string().optional(),
|
||||
url: urlSchema,
|
||||
summary: z.string().optional(),
|
||||
location: locationSchema.optional(),
|
||||
profiles: z.array(profileSchema).optional(),
|
||||
});
|
||||
|
||||
const workSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
position: z.string().optional(),
|
||||
url: urlSchema,
|
||||
startDate: iso8601.optional(),
|
||||
endDate: iso8601.optional(),
|
||||
summary: z.string().optional(),
|
||||
highlights: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const volunteerSchema = z.object({
|
||||
organization: z.string().optional(),
|
||||
position: z.string().optional(),
|
||||
url: urlSchema,
|
||||
startDate: iso8601.optional(),
|
||||
endDate: iso8601.optional(),
|
||||
summary: z.string().optional(),
|
||||
highlights: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const awardsSchema = z.object({
|
||||
title: z.string().optional(),
|
||||
date: iso8601.optional(),
|
||||
awarder: z.string().optional(),
|
||||
summary: z.string().optional(),
|
||||
});
|
||||
|
||||
const certificatesSchema = z.object({
|
||||
title: z.string().optional(),
|
||||
date: iso8601.optional(),
|
||||
issuer: z.string().optional(),
|
||||
summary: z.string().optional(),
|
||||
});
|
||||
|
||||
const educationSchema = z.object({
|
||||
institution: z.string().optional(),
|
||||
url: urlSchema,
|
||||
area: z.string().optional(),
|
||||
studyType: z.string().optional(),
|
||||
startDate: iso8601.optional(),
|
||||
endDate: iso8601.optional(),
|
||||
score: z.string().optional(),
|
||||
courses: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const publicationsSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
publisher: z.string().optional(),
|
||||
releaseDate: iso8601.optional(),
|
||||
url: urlSchema,
|
||||
summary: z.string().optional(),
|
||||
});
|
||||
|
||||
const skillsSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
level: z.string().optional(),
|
||||
keywords: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const languagesSchema = z.object({
|
||||
language: z.string().optional(),
|
||||
fluency: z.string().optional(),
|
||||
});
|
||||
|
||||
const interestsSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
keywords: z.array(z.string()).optional(),
|
||||
});
|
||||
|
||||
const referencesSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
reference: z.string().optional(),
|
||||
});
|
||||
|
||||
export const jsonResumeSchema = z.object({
|
||||
basics: basicsSchema.optional(),
|
||||
work: z.array(workSchema).optional(),
|
||||
volunteer: z.array(volunteerSchema).optional(),
|
||||
education: z.array(educationSchema).optional(),
|
||||
awards: z.array(awardsSchema).optional(),
|
||||
certificates: z.array(certificatesSchema).optional(),
|
||||
publications: z.array(publicationsSchema).optional(),
|
||||
skills: z.array(skillsSchema).optional(),
|
||||
languages: z.array(languagesSchema).optional(),
|
||||
interests: z.array(interestsSchema).optional(),
|
||||
references: z.array(referencesSchema).optional(),
|
||||
});
|
||||
|
||||
export type JsonResume = z.infer<typeof jsonResumeSchema>;
|
||||
168
libs/parser/src/linkedin/index.ts
Normal file
168
libs/parser/src/linkedin/index.ts
Normal file
@ -0,0 +1,168 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import {
|
||||
defaultCertification,
|
||||
defaultEducation,
|
||||
defaultExperience,
|
||||
defaultLanguage,
|
||||
defaultProfile,
|
||||
defaultProject,
|
||||
defaultResumeData,
|
||||
defaultSkill,
|
||||
ResumeData,
|
||||
resumeDataSchema,
|
||||
} from "@reactive-resume/schema";
|
||||
import { extractUrl, Json, parseCSV } from "@reactive-resume/utils";
|
||||
import * as JSZip from "jszip";
|
||||
import { Schema } from "zod";
|
||||
|
||||
import { Parser } from "../interfaces/parser";
|
||||
import { LinkedIn, linkedInSchema } from "./schema";
|
||||
|
||||
export * from "./schema";
|
||||
|
||||
export class LinkedInParser implements Parser<JSZip, LinkedIn> {
|
||||
schema: Schema;
|
||||
|
||||
constructor() {
|
||||
this.schema = linkedInSchema;
|
||||
}
|
||||
|
||||
async readFile(file: File): Promise<JSZip> {
|
||||
const data = await JSZip.loadAsync(file);
|
||||
|
||||
if (Object.keys(data.files).length === 0) {
|
||||
throw new Error("ParserError: There were no files found inside the zip archive.");
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
async validate(data: JSZip) {
|
||||
const result: Json = {};
|
||||
|
||||
for (const [name, file] of Object.entries(data.files)) {
|
||||
for (const key of Object.keys(linkedInSchema.shape)) {
|
||||
if (name.includes(key)) {
|
||||
const content = await file.async("text");
|
||||
const jsonArray = await parseCSV(content);
|
||||
result[key] = jsonArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return linkedInSchema.parse(result);
|
||||
}
|
||||
|
||||
convert(data: LinkedIn) {
|
||||
const result = JSON.parse(JSON.stringify(defaultResumeData)) as ResumeData;
|
||||
|
||||
// Profile
|
||||
if (data.Profile && data.Profile.length > 0) {
|
||||
const profile = data.Profile[0];
|
||||
const twitterHandle = profile["Twitter Handles"];
|
||||
|
||||
result.basics.name = `${profile["First Name"]} ${profile["Last Name"]}`;
|
||||
result.basics.location = profile["Geo Location"];
|
||||
result.basics.headline = profile.Headline;
|
||||
result.basics.url.href = extractUrl(profile.Websites) ?? "";
|
||||
result.sections.summary.content = profile.Summary;
|
||||
result.sections.profiles.items.push({
|
||||
...defaultProfile,
|
||||
id: createId(),
|
||||
icon: "twitter",
|
||||
network: "Twitter",
|
||||
username: twitterHandle,
|
||||
url: { ...defaultProfile.url, href: `https://twitter.com/${twitterHandle}` },
|
||||
});
|
||||
}
|
||||
|
||||
// Email Addresses
|
||||
if (data["Email Addresses"] && data["Email Addresses"].length > 0) {
|
||||
const email = data["Email Addresses"][0];
|
||||
|
||||
result.basics.email = email["Email Address"];
|
||||
}
|
||||
|
||||
// Positions
|
||||
if (data["Positions"] && data["Positions"].length > 0) {
|
||||
for (const position of data["Positions"]) {
|
||||
result.sections.experience.items.push({
|
||||
...defaultExperience,
|
||||
id: createId(),
|
||||
company: position["Company Name"],
|
||||
position: position.Title,
|
||||
location: position.Location,
|
||||
summary: position.Description ?? "",
|
||||
date: `${position["Started On"]} - ${position["Finished On"] ?? "Present"}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Education
|
||||
if (data["Education"] && data["Education"].length > 0) {
|
||||
for (const education of data["Education"]) {
|
||||
result.sections.education.items.push({
|
||||
...defaultEducation,
|
||||
id: createId(),
|
||||
institution: education["School Name"],
|
||||
studyType: education["Degree Name"],
|
||||
summary: education.Notes ?? "",
|
||||
date: `${education["Start Date"]} - ${education["End Date"] ?? "Present"}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Skills
|
||||
if (data["Skills"] && data["Skills"].length > 0) {
|
||||
for (const skill of data["Skills"]) {
|
||||
result.sections.skills.items.push({
|
||||
...defaultSkill,
|
||||
id: createId(),
|
||||
name: skill.Name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Languages
|
||||
if (data["Languages"] && data["Languages"].length > 0) {
|
||||
for (const language of data["Languages"]) {
|
||||
result.sections.languages.items.push({
|
||||
...defaultLanguage,
|
||||
id: createId(),
|
||||
name: language.Name,
|
||||
fluency: language.Proficiency ?? "",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Certifications
|
||||
if (data["Certifications"] && data["Certifications"].length > 0) {
|
||||
for (const certification of data["Certifications"]) {
|
||||
result.sections.certifications.items.push({
|
||||
...defaultCertification,
|
||||
id: createId(),
|
||||
name: certification.Name,
|
||||
issuer: certification.Authority,
|
||||
url: { ...defaultCertification.url, href: certification.Url },
|
||||
date: `${certification["Started On"]} - ${certification["Finished On"] ?? "Present"}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Projects
|
||||
if (data["Projects"] && data["Projects"].length > 0) {
|
||||
for (const project of data["Projects"]) {
|
||||
result.sections.projects.items.push({
|
||||
...defaultProject,
|
||||
id: createId(),
|
||||
name: project.Title,
|
||||
description: project.Description,
|
||||
url: { ...defaultProject.url, href: project.Url },
|
||||
date: `${project["Started On"]} - ${project["Finished On"] ?? "Present"}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return resumeDataSchema.parse(result);
|
||||
}
|
||||
}
|
||||
10
libs/parser/src/linkedin/schema/certification.ts
Normal file
10
libs/parser/src/linkedin/schema/certification.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const certificationSchema = z.object({
|
||||
Name: z.string(),
|
||||
Url: z.string().url(),
|
||||
Authority: z.string(),
|
||||
"Started On": z.string(),
|
||||
"Finished On": z.string().optional(),
|
||||
"License Number": z.string(),
|
||||
});
|
||||
10
libs/parser/src/linkedin/schema/education.ts
Normal file
10
libs/parser/src/linkedin/schema/education.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const educationSchema = z.object({
|
||||
"School Name": z.string(),
|
||||
"Start Date": z.string(),
|
||||
"End Date": z.string(),
|
||||
Notes: z.string().optional(),
|
||||
"Degree Name": z.string(),
|
||||
Activities: z.string(),
|
||||
});
|
||||
8
libs/parser/src/linkedin/schema/email.ts
Normal file
8
libs/parser/src/linkedin/schema/email.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const emailSchema = z.object({
|
||||
"Email Address": z.string().email(),
|
||||
Confirmed: z.enum(["Yes", "No"]),
|
||||
Primary: z.enum(["Yes", "No"]),
|
||||
"Updated On": z.string(),
|
||||
});
|
||||
23
libs/parser/src/linkedin/schema/index.ts
Normal file
23
libs/parser/src/linkedin/schema/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { certificationSchema } from "./certification";
|
||||
import { educationSchema } from "./education";
|
||||
import { emailSchema } from "./email";
|
||||
import { languageSchema } from "./language";
|
||||
import { positionSchema } from "./position";
|
||||
import { profileSchema } from "./profile";
|
||||
import { projectSchema } from "./project";
|
||||
import { skillSchema } from "./skill";
|
||||
|
||||
export const linkedInSchema = z.object({
|
||||
Profile: z.array(profileSchema).optional(),
|
||||
"Email Addresses": z.array(emailSchema).optional(),
|
||||
Certifications: z.array(certificationSchema).optional(),
|
||||
Education: z.array(educationSchema).optional(),
|
||||
Languages: z.array(languageSchema).optional(),
|
||||
Positions: z.array(positionSchema).optional(),
|
||||
Projects: z.array(projectSchema).optional(),
|
||||
Skills: z.array(skillSchema).optional(),
|
||||
});
|
||||
|
||||
export type LinkedIn = z.infer<typeof linkedInSchema>;
|
||||
6
libs/parser/src/linkedin/schema/language.ts
Normal file
6
libs/parser/src/linkedin/schema/language.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const languageSchema = z.object({
|
||||
Name: z.string(),
|
||||
Proficiency: z.string().optional(),
|
||||
});
|
||||
10
libs/parser/src/linkedin/schema/position.ts
Normal file
10
libs/parser/src/linkedin/schema/position.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const positionSchema = z.object({
|
||||
"Company Name": z.string(),
|
||||
Title: z.string(),
|
||||
Description: z.string().optional(),
|
||||
Location: z.string(),
|
||||
"Started On": z.string(),
|
||||
"Finished On": z.string().optional(),
|
||||
});
|
||||
17
libs/parser/src/linkedin/schema/profile.ts
Normal file
17
libs/parser/src/linkedin/schema/profile.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const profileSchema = z.object({
|
||||
"First Name": z.string(),
|
||||
"Last Name": z.string(),
|
||||
"Maiden Name": z.string().optional(),
|
||||
Address: z.string(),
|
||||
"Birth Date": z.string(),
|
||||
Headline: z.string(),
|
||||
Summary: z.string(),
|
||||
Industry: z.string(),
|
||||
"Zip Code": z.string().optional(),
|
||||
"Geo Location": z.string(),
|
||||
"Twitter Handles": z.string(),
|
||||
Websites: z.string(),
|
||||
"Instant Messengers": z.string().optional(),
|
||||
});
|
||||
9
libs/parser/src/linkedin/schema/project.ts
Normal file
9
libs/parser/src/linkedin/schema/project.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const projectSchema = z.object({
|
||||
Title: z.string(),
|
||||
Description: z.string(),
|
||||
Url: z.string().url(),
|
||||
"Started On": z.string(),
|
||||
"Finished On": z.string().optional(),
|
||||
});
|
||||
5
libs/parser/src/linkedin/schema/skill.ts
Normal file
5
libs/parser/src/linkedin/schema/skill.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const skillSchema = z.object({
|
||||
Name: z.string(),
|
||||
});
|
||||
248
libs/parser/src/reactive-resume-v3/index.ts
Normal file
248
libs/parser/src/reactive-resume-v3/index.ts
Normal file
@ -0,0 +1,248 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import {
|
||||
defaultAward,
|
||||
defaultCertification,
|
||||
defaultEducation,
|
||||
defaultExperience,
|
||||
defaultInterest,
|
||||
defaultLanguage,
|
||||
defaultProfile,
|
||||
defaultProject,
|
||||
defaultPublication,
|
||||
defaultReference,
|
||||
defaultResumeData,
|
||||
defaultSkill,
|
||||
defaultVolunteer,
|
||||
ResumeData,
|
||||
} from "@reactive-resume/schema";
|
||||
import { isUrl, Json } from "@reactive-resume/utils";
|
||||
import { Schema } from "zod";
|
||||
|
||||
import { Parser } from "../interfaces/parser";
|
||||
import { ReactiveResumeV3, reactiveResumeV3Schema } from "./schema";
|
||||
|
||||
export * from "./schema";
|
||||
|
||||
export class ReactiveResumeV3Parser implements Parser<Json, ReactiveResumeV3> {
|
||||
schema: Schema;
|
||||
|
||||
constructor() {
|
||||
this.schema = reactiveResumeV3Schema;
|
||||
}
|
||||
|
||||
readFile(file: File): Promise<Json> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result as string) as Json;
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(new Error("Failed to parse JSON"));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = () => {
|
||||
reject(new Error("Failed to read the file"));
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
validate(data: Json) {
|
||||
return this.schema.parse(data) as ReactiveResumeV3;
|
||||
}
|
||||
|
||||
convert(data: ReactiveResumeV3) {
|
||||
const result = JSON.parse(JSON.stringify(defaultResumeData)) as ResumeData;
|
||||
|
||||
// Basics
|
||||
result.basics.name = data.basics.name;
|
||||
result.basics.email = data.basics.email;
|
||||
result.basics.phone = data.basics.phone;
|
||||
result.basics.headline = data.basics.headline;
|
||||
result.basics.location = data.basics.location.address;
|
||||
result.sections.summary.content = data.basics.summary;
|
||||
result.basics.picture.url = isUrl(data.basics.photo.url) ? data.basics.photo.url : "";
|
||||
|
||||
// Profiles
|
||||
if (data.basics.profiles) {
|
||||
for (const profile of data.basics.profiles) {
|
||||
result.sections.profiles.items.push({
|
||||
...defaultProfile,
|
||||
id: createId(),
|
||||
network: profile.network,
|
||||
username: profile.username,
|
||||
icon: profile.network.toLocaleLowerCase(),
|
||||
url: { ...defaultProfile.url, href: isUrl(profile.url) ? profile.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Work
|
||||
if (data.sections.work.items) {
|
||||
for (const work of data.sections.work.items) {
|
||||
result.sections.experience.items.push({
|
||||
...defaultExperience,
|
||||
id: createId(),
|
||||
company: work.name,
|
||||
position: work.position,
|
||||
summary: work.summary,
|
||||
date: `${work.date.start} - ${work.date.end}`,
|
||||
url: { ...defaultExperience.url, href: isUrl(work.url) ? work.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Awards
|
||||
if (data.sections.awards.items) {
|
||||
for (const award of data.sections.awards.items) {
|
||||
result.sections.awards.items.push({
|
||||
...defaultAward,
|
||||
id: createId(),
|
||||
title: award.title,
|
||||
awarder: award.awarder,
|
||||
date: award.date,
|
||||
summary: award.summary,
|
||||
url: { ...defaultAward.url, href: isUrl(award.url) ? award.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Skills
|
||||
if (data.sections.skills.items) {
|
||||
for (const skill of data.sections.skills.items) {
|
||||
result.sections.skills.items.push({
|
||||
...defaultSkill,
|
||||
id: createId(),
|
||||
name: skill.name,
|
||||
level: Math.floor(skill.levelNum / 2),
|
||||
description: skill.level,
|
||||
keywords: skill.keywords,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Projects
|
||||
if (data.sections.projects.items) {
|
||||
for (const project of data.sections.projects.items) {
|
||||
result.sections.projects.items.push({
|
||||
...defaultProject,
|
||||
id: createId(),
|
||||
name: project.name,
|
||||
summary: project.summary,
|
||||
description: project.description,
|
||||
date: `${project.date.start} - ${project.date.end}`,
|
||||
keywords: project.keywords,
|
||||
url: { ...defaultProject.url, href: isUrl(project.url) ? project.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Education
|
||||
if (data.sections.education.items) {
|
||||
for (const education of data.sections.education.items) {
|
||||
result.sections.education.items.push({
|
||||
...defaultEducation,
|
||||
id: createId(),
|
||||
institution: education.institution,
|
||||
studyType: education.degree,
|
||||
area: education.area,
|
||||
score: education.score,
|
||||
summary: education.summary,
|
||||
date: `${education.date.start} - ${education.date.end}`,
|
||||
url: { ...defaultEducation.url, href: isUrl(education.url) ? education.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Interests
|
||||
if (data.sections.interests.items) {
|
||||
for (const interest of data.sections.interests.items) {
|
||||
result.sections.interests.items.push({
|
||||
...defaultInterest,
|
||||
id: createId(),
|
||||
name: interest.name,
|
||||
keywords: interest.keywords,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Languages
|
||||
if (data.sections.languages.items) {
|
||||
for (const language of data.sections.languages.items) {
|
||||
result.sections.languages.items.push({
|
||||
...defaultLanguage,
|
||||
id: createId(),
|
||||
name: language.name,
|
||||
fluency: language.level,
|
||||
fluencyLevel: Math.floor(language.levelNum / 2),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Volunteer
|
||||
if (data.sections.volunteer.items) {
|
||||
for (const volunteer of data.sections.volunteer.items) {
|
||||
result.sections.volunteer.items.push({
|
||||
...defaultVolunteer,
|
||||
id: createId(),
|
||||
organization: volunteer.organization,
|
||||
position: volunteer.position,
|
||||
summary: volunteer.summary,
|
||||
date: `${volunteer.date.start} - ${volunteer.date.end}`,
|
||||
url: { ...defaultVolunteer.url, href: isUrl(volunteer.url) ? volunteer.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// References
|
||||
if (data.sections.references.items) {
|
||||
for (const reference of data.sections.references.items) {
|
||||
result.sections.references.items.push({
|
||||
...defaultReference,
|
||||
id: createId(),
|
||||
name: reference.name,
|
||||
summary: reference.summary,
|
||||
description: reference.relationship,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Publications
|
||||
if (data.sections.publications.items) {
|
||||
for (const publication of data.sections.publications.items) {
|
||||
result.sections.publications.items.push({
|
||||
...defaultPublication,
|
||||
id: createId(),
|
||||
name: publication.name,
|
||||
summary: publication.summary,
|
||||
date: publication.date,
|
||||
url: { ...defaultPublication.url, href: isUrl(publication.url) ? publication.url : "" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Certifications
|
||||
if (data.sections.certifications.items) {
|
||||
for (const certification of data.sections.certifications.items) {
|
||||
result.sections.certifications.items.push({
|
||||
...defaultCertification,
|
||||
id: createId(),
|
||||
name: certification.name,
|
||||
issuer: certification.issuer,
|
||||
summary: certification.summary,
|
||||
date: certification.date,
|
||||
url: {
|
||||
...defaultCertification.url,
|
||||
href: isUrl(certification.url) ? certification.url : "",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
179
libs/parser/src/reactive-resume-v3/schema.ts
Normal file
179
libs/parser/src/reactive-resume-v3/schema.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const dateSchema = z.object({ start: z.string(), end: z.string() });
|
||||
|
||||
const profileSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
network: z.string(),
|
||||
username: z.string(),
|
||||
});
|
||||
|
||||
const basicsSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.literal("").or(z.string().email()),
|
||||
phone: z.string(),
|
||||
headline: z.string(),
|
||||
summary: z.string(),
|
||||
birthdate: z.string(),
|
||||
website: z.string(),
|
||||
profiles: z.array(profileSchema),
|
||||
location: z.object({
|
||||
address: z.string(),
|
||||
postalCode: z.string(),
|
||||
city: z.string(),
|
||||
country: z.string(),
|
||||
region: z.string(),
|
||||
}),
|
||||
photo: z.object({
|
||||
visible: z.boolean(),
|
||||
url: z.string(),
|
||||
filters: z.object({
|
||||
shape: z.string(),
|
||||
size: z.number(),
|
||||
border: z.boolean(),
|
||||
grayscale: z.boolean(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const sectionSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
type: z.enum(["basic", "custom"]),
|
||||
columns: z.number(),
|
||||
visible: z.boolean(),
|
||||
});
|
||||
|
||||
const workSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
date: dateSchema,
|
||||
name: z.string(),
|
||||
position: z.string(),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
const awardSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
date: z.string(),
|
||||
title: z.string(),
|
||||
awarder: z.string(),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
const skillSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
level: z.string(),
|
||||
keywords: z.array(z.string()),
|
||||
levelNum: z.number(),
|
||||
});
|
||||
|
||||
const projectSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
date: dateSchema,
|
||||
name: z.string(),
|
||||
summary: z.string(),
|
||||
keywords: z.array(z.string()),
|
||||
description: z.string(),
|
||||
});
|
||||
|
||||
const educationSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
area: z.string(),
|
||||
date: dateSchema,
|
||||
score: z.string(),
|
||||
degree: z.string(),
|
||||
courses: z.array(z.string()),
|
||||
summary: z.string(),
|
||||
institution: z.string(),
|
||||
});
|
||||
|
||||
const interestSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
keywords: z.array(z.string()),
|
||||
});
|
||||
|
||||
const languageSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
level: z.string(),
|
||||
levelNum: z.number(),
|
||||
});
|
||||
|
||||
const volunteerSchema = z.object({
|
||||
id: z.string(),
|
||||
organization: z.string(),
|
||||
position: z.string(),
|
||||
date: dateSchema,
|
||||
url: z.string(),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
const referenceSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
phone: z.string(),
|
||||
summary: z.string(),
|
||||
relationship: z.string(),
|
||||
});
|
||||
|
||||
const publicationSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
date: z.string(),
|
||||
name: z.string(),
|
||||
publisher: z.string(),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
const certificationSchema = z.object({
|
||||
id: z.string(),
|
||||
url: z.string(),
|
||||
date: z.string(),
|
||||
name: z.string(),
|
||||
issuer: z.string(),
|
||||
summary: z.string(),
|
||||
});
|
||||
|
||||
const metadataSchema = z.object({
|
||||
css: z.object({ value: z.string(), visible: z.boolean() }),
|
||||
date: z.object({ format: z.string() }),
|
||||
theme: z.object({ text: z.string(), primary: z.string(), background: z.string() }),
|
||||
layout: z.array(z.array(z.array(z.string()))),
|
||||
locale: z.string(),
|
||||
template: z.string(),
|
||||
typography: z.object({
|
||||
size: z.object({ body: z.number(), heading: z.number() }),
|
||||
family: z.object({ body: z.string(), heading: z.string() }),
|
||||
}),
|
||||
});
|
||||
|
||||
export const reactiveResumeV3Schema = z.object({
|
||||
public: z.boolean(),
|
||||
basics: basicsSchema,
|
||||
sections: z.object({
|
||||
work: sectionSchema.extend({ items: z.array(workSchema) }),
|
||||
awards: sectionSchema.extend({ items: z.array(awardSchema) }),
|
||||
skills: sectionSchema.extend({ items: z.array(skillSchema) }),
|
||||
projects: sectionSchema.extend({ items: z.array(projectSchema) }),
|
||||
education: sectionSchema.extend({ items: z.array(educationSchema) }),
|
||||
interests: sectionSchema.extend({ items: z.array(interestSchema) }),
|
||||
languages: sectionSchema.extend({ items: z.array(languageSchema) }),
|
||||
volunteer: sectionSchema.extend({ items: z.array(volunteerSchema) }),
|
||||
references: sectionSchema.extend({ items: z.array(referenceSchema) }),
|
||||
publications: sectionSchema.extend({ items: z.array(publicationSchema) }),
|
||||
certifications: sectionSchema.extend({
|
||||
items: z.array(certificationSchema),
|
||||
}),
|
||||
}),
|
||||
metadata: metadataSchema,
|
||||
});
|
||||
|
||||
export type ReactiveResumeV3 = z.infer<typeof reactiveResumeV3Schema>;
|
||||
42
libs/parser/src/reactive-resume/index.ts
Normal file
42
libs/parser/src/reactive-resume/index.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { ResumeData, resumeDataSchema } from "@reactive-resume/schema";
|
||||
import { Json } from "@reactive-resume/utils";
|
||||
import { Schema } from "zod";
|
||||
|
||||
import { Parser } from "../interfaces/parser";
|
||||
|
||||
export class ReactiveResumeParser implements Parser<Json, ResumeData> {
|
||||
schema: Schema;
|
||||
|
||||
constructor() {
|
||||
this.schema = resumeDataSchema;
|
||||
}
|
||||
|
||||
readFile(file: File): Promise<Json> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result as string) as Json;
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
reject(new Error("Failed to parse JSON"));
|
||||
}
|
||||
};
|
||||
|
||||
reader.onerror = () => {
|
||||
reject(new Error("Failed to read the file"));
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
});
|
||||
}
|
||||
|
||||
validate(data: Json) {
|
||||
return this.schema.parse(data) as ResumeData;
|
||||
}
|
||||
|
||||
convert(data: ResumeData) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
22
libs/parser/tsconfig.json
Normal file
22
libs/parser/tsconfig.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
10
libs/parser/tsconfig.lib.json
Normal file
10
libs/parser/tsconfig.lib.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
|
||||
}
|
||||
19
libs/parser/tsconfig.spec.json
Normal file
19
libs/parser/tsconfig.spec.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"]
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
15
libs/parser/vite.config.ts
Normal file
15
libs/parser/vite.config.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
cacheDir: "../../node_modules/.vite/parser",
|
||||
|
||||
plugins: [nxViteTsPaths()],
|
||||
|
||||
test: {
|
||||
globals: true,
|
||||
environment: "jsdom",
|
||||
cache: { dir: "../../node_modules/.vitest" },
|
||||
include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user