chore: 初始化项目骨架

This commit is contained in:
SkyJourney
2026-07-25 16:51:16 +08:00
commit ec48bce0a6
26 changed files with 3806 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
{
"name": "@md-to-pdf/core",
"version": "0.1.0",
"private": true,
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"files": [
"dist"
],
"scripts": {
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput",
"build": "tsc -p tsconfig.json",
"typecheck": "tsc -p tsconfig.json --noEmit --pretty false"
},
"dependencies": {
"zod": "^4.1.13"
}
}
+175
View File
@@ -0,0 +1,175 @@
import { z } from "zod";
export const EXPORT_CONFIG_VERSION = 1;
export const supportedPaperFormats = [
"A3",
"A4",
"A5",
"Letter",
"Legal",
"Tabloid",
"Custom"
] as const;
export const paperFormatLabels: Record<
(typeof supportedPaperFormats)[number],
string
> = {
A3: "A3",
A4: "A4",
A5: "A5",
Letter: "Letter",
Legal: "Legal",
Tabloid: "Tabloid",
Custom: "自定义"
};
const lengthSchema = z
.string()
.regex(/^\d+(?:\.\d+)?(?:mm|cm|in)$/, "长度必须包含 mm、cm 或 in 单位");
const marginSchema = z.object({
top: lengthSchema,
right: lengthSchema,
bottom: lengthSchema,
left: lengthSchema
});
const headerFooterSlotSchema = z.object({
enabled: z.boolean(),
content: z.string().max(500)
});
const headerFooterSchema = z.object({
enabled: z.boolean(),
height: lengthSchema,
showDivider: z.boolean(),
fontSize: lengthSchema,
color: z.string(),
left: headerFooterSlotSchema,
center: headerFooterSlotSchema,
right: headerFooterSlotSchema
});
const paperSchema = z
.object({
format: z.enum(supportedPaperFormats),
orientation: z.enum(["portrait", "landscape"]),
width: lengthSchema.optional(),
height: lengthSchema.optional(),
margins: marginSchema
})
.superRefine((paper, context) => {
if (paper.format === "Custom" && (!paper.width || !paper.height)) {
context.addIssue({
code: "custom",
message: "自定义纸张必须同时指定宽度和高度",
path: ["width"]
});
}
});
export const pageNumberFormatSchema = z.enum([
"page",
"page-total",
"chinese-page-total",
"dash-page",
"custom"
]);
export const exportConfigSchema = z.object({
version: z.literal(EXPORT_CONFIG_VERSION),
name: z.string().min(1).max(100),
themeId: z.string().min(1),
paper: paperSchema,
header: headerFooterSchema,
footer: headerFooterSchema,
pageNumber: z.object({
enabled: z.boolean(),
position: z.enum(["header", "footer"]),
alignment: z.enum(["left", "center", "right"]),
format: pageNumberFormatSchema,
template: z.string().max(500).optional(),
startFrom: z.number().int().positive()
}),
metadata: z.object({
title: z.string().max(300),
author: z.string().max(300),
subject: z.string().max(500),
keywords: z.array(z.string().max(100)).max(30),
language: z.string().max(30)
}),
print: z.object({
background: z.boolean(),
scale: z.number().min(0.1).max(2),
pageBreakBeforeH1: z.boolean()
})
});
export type ExportConfig = z.infer<typeof exportConfigSchema>;
export type PaperFormat = ExportConfig["paper"]["format"];
export type HeaderFooterConfig = ExportConfig["header"];
const disabledSlot = {
enabled: false,
content: ""
} as const;
export const defaultExportConfig: ExportConfig = {
version: EXPORT_CONFIG_VERSION,
name: "默认 A4 技术文档",
themeId: "typora-like",
paper: {
format: "A4",
orientation: "portrait",
margins: {
top: "20mm",
right: "18mm",
bottom: "20mm",
left: "18mm"
}
},
header: {
enabled: false,
height: "8mm",
showDivider: false,
fontSize: "3mm",
color: "#6b7280",
left: disabledSlot,
center: disabledSlot,
right: disabledSlot
},
footer: {
enabled: true,
height: "8mm",
showDivider: false,
fontSize: "3mm",
color: "#6b7280",
left: disabledSlot,
center: {
enabled: true,
content: "${page} / ${pages}"
},
right: disabledSlot
},
pageNumber: {
enabled: true,
position: "footer",
alignment: "center",
format: "page-total",
startFrom: 1
},
metadata: {
title: "",
author: "",
subject: "",
keywords: [],
language: "zh-CN"
},
print: {
background: true,
scale: 1,
pageBreakBeforeH1: false
}
};
+2
View File
@@ -0,0 +1,2 @@
export * from "./export-config.js";
export * from "./theme.js";
+31
View File
@@ -0,0 +1,31 @@
import { z } from "zod";
export const THEME_MANIFEST_VERSION = 1;
export const themeFeatureSchema = z.enum([
"code",
"table",
"task-list",
"footnote",
"katex",
"mermaid"
]);
export const themeManifestSchema = z.object({
manifestVersion: z.literal(THEME_MANIFEST_VERSION),
id: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
name: z.string().min(1).max(100),
version: z.string().min(1).max(30),
description: z.string().max(500),
author: z.string().max(100),
license: z.string().max(100),
entry: z.string().endsWith(".css"),
print: z.string().endsWith(".css").optional(),
domPreset: z.enum(["typora", "github", "generic"]),
defaultFontSize: z.string(),
supportedFeatures: z.array(themeFeatureSchema),
bundled: z.boolean()
});
export type ThemeManifest = z.infer<typeof themeManifestSchema>;
export type ThemeFeature = z.infer<typeof themeFeatureSchema>;
+16
View File
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": [
"src"
]
}