@openeditor/exporters 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +175 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# `@openeditor/exporters`
|
|
2
|
+
|
|
3
|
+
Pure import/export helpers for OpenEditor documents.
|
|
4
|
+
|
|
5
|
+
## Public surface
|
|
6
|
+
|
|
7
|
+
- JSON, HTML, and plain text export helpers
|
|
8
|
+
- JSON import and validation helpers
|
|
9
|
+
|
|
10
|
+
## Internal notes
|
|
11
|
+
|
|
12
|
+
- Exporters remain side-effect free and renderer-independent.
|
|
13
|
+
- Keep format transforms here rather than in `@openeditor/web` or `@openeditor/native`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { OpenEditorDocument, DocumentValidationResult } from '@openeditor/core';
|
|
2
|
+
|
|
3
|
+
type ExportFormat = "json" | "html" | "text";
|
|
4
|
+
type ExportBundle = {
|
|
5
|
+
json: string;
|
|
6
|
+
html: string;
|
|
7
|
+
text: string;
|
|
8
|
+
};
|
|
9
|
+
type JsonImportResult = {
|
|
10
|
+
document: OpenEditorDocument;
|
|
11
|
+
validation: DocumentValidationResult;
|
|
12
|
+
};
|
|
13
|
+
declare const toJson: (document: OpenEditorDocument) => string;
|
|
14
|
+
declare const toPlainText: (document: OpenEditorDocument) => string;
|
|
15
|
+
declare const toHtml: (document: OpenEditorDocument) => string;
|
|
16
|
+
declare const toExportBundle: (document: OpenEditorDocument) => ExportBundle;
|
|
17
|
+
declare const exportDocument: (document: OpenEditorDocument, format: ExportFormat) => string;
|
|
18
|
+
declare const fromJson: (value: string | unknown, defaultDocument?: OpenEditorDocument) => OpenEditorDocument;
|
|
19
|
+
declare const parseJsonExport: (value: string | unknown, defaultDocument?: OpenEditorDocument) => JsonImportResult;
|
|
20
|
+
|
|
21
|
+
export { type ExportBundle, type ExportFormat, type JsonImportResult, exportDocument, fromJson, parseJsonExport, toExportBundle, toHtml, toJson, toPlainText };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { normalizeDocument, parseOpenEditorDocument, validateDocument, getDocumentText } from '@openeditor/core';
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
var escapeHtml = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
|
|
5
|
+
var escapeAttribute = (value) => escapeHtml(value).replaceAll("'", "'");
|
|
6
|
+
var commonAttributesToHtml = (attrs, extraAttributes = []) => {
|
|
7
|
+
const names = ["id", "class", "title", "lang", "dir", "data-id", "style", "align", "width", "height", ...extraAttributes];
|
|
8
|
+
const rendered = names.map((name) => {
|
|
9
|
+
const value = attrs?.[name];
|
|
10
|
+
return typeof value === "string" || typeof value === "number" ? `${name}="${escapeAttribute(String(value))}"` : "";
|
|
11
|
+
}).filter(Boolean).join(" ");
|
|
12
|
+
return rendered ? ` ${rendered}` : "";
|
|
13
|
+
};
|
|
14
|
+
var markToHtml = (html, mark) => {
|
|
15
|
+
switch (mark.type) {
|
|
16
|
+
case "bold":
|
|
17
|
+
return `<strong>${html}</strong>`;
|
|
18
|
+
case "italic":
|
|
19
|
+
return `<em>${html}</em>`;
|
|
20
|
+
case "underline":
|
|
21
|
+
return `<u>${html}</u>`;
|
|
22
|
+
case "strike":
|
|
23
|
+
return `<s>${html}</s>`;
|
|
24
|
+
case "code":
|
|
25
|
+
return `<code>${html}</code>`;
|
|
26
|
+
case "link": {
|
|
27
|
+
const href = typeof mark.attrs?.href === "string" ? mark.attrs.href : "";
|
|
28
|
+
const target = typeof mark.attrs?.target === "string" ? mark.attrs.target : void 0;
|
|
29
|
+
const rel = typeof mark.attrs?.rel === "string" ? mark.attrs.rel : void 0;
|
|
30
|
+
const attrs = [
|
|
31
|
+
`href="${escapeAttribute(href)}"`,
|
|
32
|
+
target ? `target="${escapeAttribute(target)}"` : "",
|
|
33
|
+
rel ? `rel="${escapeAttribute(rel)}"` : ""
|
|
34
|
+
].filter(Boolean).join(" ");
|
|
35
|
+
return href ? `<a ${attrs}>${html}</a>` : html;
|
|
36
|
+
}
|
|
37
|
+
default:
|
|
38
|
+
return html;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var textNodeToHtml = (node) => (node.marks ?? []).reduce(
|
|
42
|
+
(html, mark) => markToHtml(html, mark),
|
|
43
|
+
escapeHtml(node.text ?? "")
|
|
44
|
+
);
|
|
45
|
+
var childrenToHtml = (node) => node.content?.map(nodeToHtml).join("") ?? textNodeToHtml(node);
|
|
46
|
+
var textContent = (node) => node.text ?? node.content?.map(textContent).join("") ?? "";
|
|
47
|
+
var nodeToHtml = (node) => {
|
|
48
|
+
switch (node.type) {
|
|
49
|
+
case "heading": {
|
|
50
|
+
const level = typeof node.attrs?.level === "number" ? node.attrs.level : 2;
|
|
51
|
+
const safeLevel = Math.min(Math.max(level, 1), 6);
|
|
52
|
+
return `<h${safeLevel}${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</h${safeLevel}>`;
|
|
53
|
+
}
|
|
54
|
+
case "paragraph":
|
|
55
|
+
return `<p${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</p>`;
|
|
56
|
+
case "bulletList":
|
|
57
|
+
return `<ul${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ul>`;
|
|
58
|
+
case "orderedList": {
|
|
59
|
+
const start = typeof node.attrs?.start === "number" && node.attrs.start !== 1 ? ` start="${node.attrs.start}"` : "";
|
|
60
|
+
return `<ol${start}${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ol>`;
|
|
61
|
+
}
|
|
62
|
+
case "taskList":
|
|
63
|
+
return `<ul data-openeditor-task-list${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ul>`;
|
|
64
|
+
case "taskItem": {
|
|
65
|
+
const checked = node.attrs?.checked ? " checked" : "";
|
|
66
|
+
return `<li data-openeditor-task-item><label><input type="checkbox"${checked} disabled /> ${childrenToHtml(node)}</label></li>`;
|
|
67
|
+
}
|
|
68
|
+
case "listItem":
|
|
69
|
+
return `<li${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</li>`;
|
|
70
|
+
case "blockquote":
|
|
71
|
+
return `<blockquote${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</blockquote>`;
|
|
72
|
+
case "codeBlock": {
|
|
73
|
+
const language = typeof node.attrs?.language === "string" ? node.attrs.language : "";
|
|
74
|
+
const className = language ? ` class="language-${escapeAttribute(language)}"` : "";
|
|
75
|
+
return `<pre><code${className}>${escapeHtml(textContent(node))}</code></pre>`;
|
|
76
|
+
}
|
|
77
|
+
case "hardBreak":
|
|
78
|
+
return "<br />";
|
|
79
|
+
case "divider":
|
|
80
|
+
case "horizontalRule":
|
|
81
|
+
return "<hr />";
|
|
82
|
+
case "image": {
|
|
83
|
+
const src = typeof node.attrs?.src === "string" ? node.attrs.src : "";
|
|
84
|
+
const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
|
|
85
|
+
return src ? `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}"${commonAttributesToHtml(node.attrs)} />` : "";
|
|
86
|
+
}
|
|
87
|
+
case "columns":
|
|
88
|
+
return `<section data-openeditor-columns="${node.attrs?.count ?? node.content?.length ?? 0}"${commonAttributesToHtml(node.attrs, ["cellspacing"])}>${childrenToHtml(node)}</section>`;
|
|
89
|
+
case "column":
|
|
90
|
+
return `<div data-openeditor-column${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</div>`;
|
|
91
|
+
case "table":
|
|
92
|
+
return `<table${commonAttributesToHtml(node.attrs, ["border", "cellpadding", "cellspacing"])}><tbody>${childrenToHtml(node)}</tbody></table>`;
|
|
93
|
+
case "tableRow":
|
|
94
|
+
return `<tr>${childrenToHtml(node)}</tr>`;
|
|
95
|
+
case "tableHeader":
|
|
96
|
+
return `<th${commonAttributesToHtml(node.attrs, ["colspan", "rowspan", "scope", "valign", "bgcolor"])}>${childrenToHtml(node)}</th>`;
|
|
97
|
+
case "tableCell":
|
|
98
|
+
return `<td${commonAttributesToHtml(node.attrs, ["colspan", "rowspan", "valign", "bgcolor"])}>${childrenToHtml(node)}</td>`;
|
|
99
|
+
case "text":
|
|
100
|
+
return textNodeToHtml(node);
|
|
101
|
+
default:
|
|
102
|
+
return childrenToHtml(node);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var toJson = (document) => JSON.stringify(normalizeDocument(document), null, 2);
|
|
106
|
+
var blockToPlainText = (node, index = 0) => {
|
|
107
|
+
switch (node.type) {
|
|
108
|
+
case "bulletList":
|
|
109
|
+
return node.content?.map((item) => `- ${getDocumentText(item)}`).join("\n") ?? "";
|
|
110
|
+
case "orderedList":
|
|
111
|
+
return node.content?.map((item, itemIndex) => `${itemIndex + 1}. ${getDocumentText(item)}`).join("\n") ?? "";
|
|
112
|
+
case "taskList":
|
|
113
|
+
return node.content?.map((item) => `${item.attrs?.checked ? "[x]" : "[ ]"} ${getDocumentText(item)}`).join("\n") ?? "";
|
|
114
|
+
case "blockquote":
|
|
115
|
+
return getDocumentText(node).split("\n").filter(Boolean).map((line) => `> ${line}`).join("\n");
|
|
116
|
+
case "codeBlock":
|
|
117
|
+
return textContent(node);
|
|
118
|
+
case "horizontalRule":
|
|
119
|
+
case "divider":
|
|
120
|
+
return "---";
|
|
121
|
+
case "columns":
|
|
122
|
+
return node.content?.map((column, columnIndex) => `Column ${columnIndex + 1}
|
|
123
|
+
${blockToPlainText(column)}`).join("\n\n") ?? "";
|
|
124
|
+
case "column":
|
|
125
|
+
return node.content?.map(blockToPlainText).filter(Boolean).join("\n\n") ?? "";
|
|
126
|
+
case "image": {
|
|
127
|
+
const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "Image";
|
|
128
|
+
return `[${alt}]`;
|
|
129
|
+
}
|
|
130
|
+
case "table":
|
|
131
|
+
return node.content?.map(
|
|
132
|
+
(row) => row.content?.map((cell) => cell.content?.map(blockToPlainText).filter(Boolean).join(" ") ?? getDocumentText(cell)).join(" | ") ?? ""
|
|
133
|
+
).join("\n") ?? "";
|
|
134
|
+
default: {
|
|
135
|
+
const text = getDocumentText(node);
|
|
136
|
+
return node.type === "listItem" ? `${index + 1}. ${text}` : text;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
var toPlainText = (document) => normalizeDocument(document).content.map(blockToPlainText).filter(Boolean).join("\n\n");
|
|
141
|
+
var toHtml = (document) => normalizeDocument(document).content.map(nodeToHtml).join("\n");
|
|
142
|
+
var toExportBundle = (document) => ({
|
|
143
|
+
json: toJson(document),
|
|
144
|
+
html: toHtml(document),
|
|
145
|
+
text: toPlainText(document)
|
|
146
|
+
});
|
|
147
|
+
var exportDocument = (document, format) => format === "json" ? toJson(document) : format === "html" ? toHtml(document) : toPlainText(document);
|
|
148
|
+
var fromJson = (value, defaultDocument) => {
|
|
149
|
+
const parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
150
|
+
return parseOpenEditorDocument(parsed, defaultDocument);
|
|
151
|
+
};
|
|
152
|
+
var parseJsonExport = (value, defaultDocument) => {
|
|
153
|
+
let parsed;
|
|
154
|
+
try {
|
|
155
|
+
parsed = typeof value === "string" ? JSON.parse(value) : value;
|
|
156
|
+
} catch {
|
|
157
|
+
const document = defaultDocument ? normalizeDocument(defaultDocument) : parseOpenEditorDocument(void 0);
|
|
158
|
+
return {
|
|
159
|
+
document,
|
|
160
|
+
validation: {
|
|
161
|
+
valid: false,
|
|
162
|
+
issues: [{ path: "$", message: "JSON could not be parsed." }]
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const validation = validateDocument(parsed);
|
|
167
|
+
return {
|
|
168
|
+
document: parseOpenEditorDocument(parsed, defaultDocument),
|
|
169
|
+
validation
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export { exportDocument, fromJson, parseJsonExport, toExportBundle, toHtml, toJson, toPlainText };
|
|
174
|
+
//# sourceMappingURL=index.js.map
|
|
175
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import {\n getDocumentText,\n normalizeDocument,\n parseOpenEditorDocument,\n validateDocument,\n type DocumentValidationResult,\n type OpenEditorDocument,\n type ProseMirrorNode,\n} from \"@openeditor/core\";\n\nexport type ExportFormat = \"json\" | \"html\" | \"text\";\n\nexport type ExportBundle = {\n json: string;\n html: string;\n text: string;\n};\n\nexport type JsonImportResult = {\n document: OpenEditorDocument;\n validation: DocumentValidationResult;\n};\n\nconst escapeHtml = (value: string) =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n\nconst escapeAttribute = (value: string) => escapeHtml(value).replaceAll(\"'\", \"'\");\n\nconst commonAttributesToHtml = (attrs?: Record<string, unknown>, extraAttributes: string[] = []) => {\n const names = [\"id\", \"class\", \"title\", \"lang\", \"dir\", \"data-id\", \"style\", \"align\", \"width\", \"height\", ...extraAttributes];\n const rendered = names\n .map((name) => {\n const value = attrs?.[name];\n return typeof value === \"string\" || typeof value === \"number\"\n ? `${name}=\"${escapeAttribute(String(value))}\"`\n : \"\";\n })\n .filter(Boolean)\n .join(\" \");\n\n return rendered ? ` ${rendered}` : \"\";\n};\n\nconst markToHtml = (html: string, mark: { type: string; attrs?: Record<string, unknown> }) => {\n switch (mark.type) {\n case \"bold\":\n return `<strong>${html}</strong>`;\n case \"italic\":\n return `<em>${html}</em>`;\n case \"underline\":\n return `<u>${html}</u>`;\n case \"strike\":\n return `<s>${html}</s>`;\n case \"code\":\n return `<code>${html}</code>`;\n case \"link\": {\n const href = typeof mark.attrs?.href === \"string\" ? mark.attrs.href : \"\";\n const target = typeof mark.attrs?.target === \"string\" ? mark.attrs.target : undefined;\n const rel = typeof mark.attrs?.rel === \"string\" ? mark.attrs.rel : undefined;\n const attrs = [\n `href=\"${escapeAttribute(href)}\"`,\n target ? `target=\"${escapeAttribute(target)}\"` : \"\",\n rel ? `rel=\"${escapeAttribute(rel)}\"` : \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n return href ? `<a ${attrs}>${html}</a>` : html;\n }\n default:\n return html;\n }\n};\n\nconst textNodeToHtml = (node: ProseMirrorNode) =>\n (node.marks ?? []).reduce(\n (html, mark) => markToHtml(html, mark),\n escapeHtml(node.text ?? \"\"),\n );\n\nconst childrenToHtml = (node: ProseMirrorNode): string =>\n node.content?.map(nodeToHtml).join(\"\") ?? textNodeToHtml(node);\n\nconst textContent = (node: ProseMirrorNode): string =>\n node.text ?? node.content?.map(textContent).join(\"\") ?? \"\";\n\nconst nodeToHtml = (node: ProseMirrorNode): string => {\n switch (node.type) {\n case \"heading\": {\n const level = typeof node.attrs?.level === \"number\" ? node.attrs.level : 2;\n const safeLevel = Math.min(Math.max(level, 1), 6);\n return `<h${safeLevel}${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</h${safeLevel}>`;\n }\n case \"paragraph\":\n return `<p${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</p>`;\n case \"bulletList\":\n return `<ul${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ul>`;\n case \"orderedList\": {\n const start = typeof node.attrs?.start === \"number\" && node.attrs.start !== 1\n ? ` start=\"${node.attrs.start}\"`\n : \"\";\n return `<ol${start}${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ol>`;\n }\n case \"taskList\":\n return `<ul data-openeditor-task-list${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</ul>`;\n case \"taskItem\": {\n const checked = node.attrs?.checked ? \" checked\" : \"\";\n return `<li data-openeditor-task-item><label><input type=\"checkbox\"${checked} disabled /> ${childrenToHtml(node)}</label></li>`;\n }\n case \"listItem\":\n return `<li${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</li>`;\n case \"blockquote\":\n return `<blockquote${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</blockquote>`;\n case \"codeBlock\": {\n const language = typeof node.attrs?.language === \"string\" ? node.attrs.language : \"\";\n const className = language ? ` class=\"language-${escapeAttribute(language)}\"` : \"\";\n return `<pre><code${className}>${escapeHtml(textContent(node))}</code></pre>`;\n }\n case \"hardBreak\":\n return \"<br />\";\n case \"divider\":\n case \"horizontalRule\":\n return \"<hr />\";\n case \"image\": {\n const src = typeof node.attrs?.src === \"string\" ? node.attrs.src : \"\";\n const alt = typeof node.attrs?.alt === \"string\" ? node.attrs.alt : \"\";\n return src ? `<img src=\"${escapeAttribute(src)}\" alt=\"${escapeAttribute(alt)}\"${commonAttributesToHtml(node.attrs)} />` : \"\";\n }\n case \"columns\":\n return `<section data-openeditor-columns=\"${node.attrs?.count ?? node.content?.length ?? 0}\"${commonAttributesToHtml(node.attrs, [\"cellspacing\"])}>${childrenToHtml(node)}</section>`;\n case \"column\":\n return `<div data-openeditor-column${commonAttributesToHtml(node.attrs)}>${childrenToHtml(node)}</div>`;\n case \"table\":\n return `<table${commonAttributesToHtml(node.attrs, [\"border\", \"cellpadding\", \"cellspacing\"])}><tbody>${childrenToHtml(node)}</tbody></table>`;\n case \"tableRow\":\n return `<tr>${childrenToHtml(node)}</tr>`;\n case \"tableHeader\":\n return `<th${commonAttributesToHtml(node.attrs, [\"colspan\", \"rowspan\", \"scope\", \"valign\", \"bgcolor\"])}>${childrenToHtml(node)}</th>`;\n case \"tableCell\":\n return `<td${commonAttributesToHtml(node.attrs, [\"colspan\", \"rowspan\", \"valign\", \"bgcolor\"])}>${childrenToHtml(node)}</td>`;\n case \"text\":\n return textNodeToHtml(node);\n default:\n return childrenToHtml(node);\n }\n};\n\nexport const toJson = (document: OpenEditorDocument): string => JSON.stringify(normalizeDocument(document), null, 2);\n\nconst blockToPlainText = (node: ProseMirrorNode, index = 0): string => {\n switch (node.type) {\n case \"bulletList\":\n return node.content?.map((item) => `- ${getDocumentText(item)}`).join(\"\\n\") ?? \"\";\n case \"orderedList\":\n return node.content?.map((item, itemIndex) => `${itemIndex + 1}. ${getDocumentText(item)}`).join(\"\\n\") ?? \"\";\n case \"taskList\":\n return node.content?.map((item) => `${item.attrs?.checked ? \"[x]\" : \"[ ]\"} ${getDocumentText(item)}`).join(\"\\n\") ?? \"\";\n case \"blockquote\":\n return getDocumentText(node)\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => `> ${line}`)\n .join(\"\\n\");\n case \"codeBlock\":\n return textContent(node);\n case \"horizontalRule\":\n case \"divider\":\n return \"---\";\n case \"columns\":\n return node.content?.map((column, columnIndex) => `Column ${columnIndex + 1}\\n${blockToPlainText(column)}`).join(\"\\n\\n\") ?? \"\";\n case \"column\":\n return node.content?.map(blockToPlainText).filter(Boolean).join(\"\\n\\n\") ?? \"\";\n case \"image\": {\n const alt = typeof node.attrs?.alt === \"string\" ? node.attrs.alt : \"Image\";\n return `[${alt}]`;\n }\n case \"table\":\n return node.content\n ?.map((row) =>\n row.content\n ?.map((cell) => cell.content?.map(blockToPlainText).filter(Boolean).join(\" \") ?? getDocumentText(cell))\n .join(\" | \") ?? \"\",\n )\n .join(\"\\n\") ?? \"\";\n default: {\n const text = getDocumentText(node);\n return node.type === \"listItem\" ? `${index + 1}. ${text}` : text;\n }\n }\n};\n\nexport const toPlainText = (document: OpenEditorDocument): string =>\n normalizeDocument(document).content.map(blockToPlainText).filter(Boolean).join(\"\\n\\n\");\n\nexport const toHtml = (document: OpenEditorDocument): string => normalizeDocument(document).content.map(nodeToHtml).join(\"\\n\");\n\nexport const toExportBundle = (document: OpenEditorDocument): ExportBundle => ({\n json: toJson(document),\n html: toHtml(document),\n text: toPlainText(document),\n});\n\nexport const exportDocument = (document: OpenEditorDocument, format: ExportFormat): string =>\n format === \"json\" ? toJson(document) : format === \"html\" ? toHtml(document) : toPlainText(document);\n\nexport const fromJson = (value: string | unknown, defaultDocument?: OpenEditorDocument): OpenEditorDocument => {\n const parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n return parseOpenEditorDocument(parsed, defaultDocument);\n};\n\nexport const parseJsonExport = (value: string | unknown, defaultDocument?: OpenEditorDocument): JsonImportResult => {\n let parsed: unknown;\n\n try {\n parsed = typeof value === \"string\" ? JSON.parse(value) : value;\n } catch {\n const document = defaultDocument ? normalizeDocument(defaultDocument) : parseOpenEditorDocument(undefined);\n return {\n document,\n validation: {\n valid: false,\n issues: [{ path: \"$\", message: \"JSON could not be parsed.\" }],\n },\n };\n }\n\n const validation = validateDocument(parsed);\n return {\n document: parseOpenEditorDocument(parsed, defaultDocument),\n validation,\n };\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openeditor/exporters",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup --config tsup.config.ts",
|
|
23
|
+
"prepack": "pnpm run build",
|
|
24
|
+
"test": "pnpm run build && node --test test/*.test.mjs",
|
|
25
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@openeditor/core": "workspace:*"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "^8.5.1",
|
|
33
|
+
"typescript": "6.0.3"
|
|
34
|
+
}
|
|
35
|
+
}
|