@portone/docx-editor 0.6.1 → 0.6.2
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/CHANGELOG.md +16 -0
- package/dist/core.d.ts +4 -3
- package/dist/docx/exportDocx.d.ts +1 -1
- package/dist/docx/exportDocx.js +3 -1
- package/dist/docx/identities.d.ts +2 -0
- package/dist/docx/identities.js +1 -0
- package/dist/docx/invariants.d.ts +13 -10
- package/dist/docx/invariants.js +269 -126
- package/dist/docx/newLists.js +12 -3
- package/dist/docx/notes/numbering.js +3 -10
- package/dist/docx/notes/references.d.ts +33 -0
- package/dist/docx/notes/references.js +37 -0
- package/dist/docx/notes/writing.js +5 -6
- package/dist/docx/serializePreserved.d.ts +5 -1
- package/dist/docx/serializePreserved.js +24 -7
- package/dist/docx/storyParts.d.ts +29 -8
- package/dist/docx/storyParts.js +40 -15
- package/dist/editor/commands/exportQueries.d.ts +4 -3
- package/dist/editor/commands/index.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/ooxml/errors.d.ts +135 -2
- package/dist/ooxml/errors.js +6 -0
- package/dist/schema/stories.d.ts +11 -3
- package/dist/schema/stories.js +9 -3
- package/package.json +1 -1
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { elementXml } from "../../ooxml/element.js";
|
|
3
3
|
import { wName } from "../../ooxml/names.js";
|
|
4
4
|
import { R_NS } from "../../ooxml/xml.js";
|
|
5
|
+
import { noteKeyOf } from "../../schema/stories.js";
|
|
5
6
|
import { storyEntriesPlanner } from "../storyParts.js";
|
|
7
|
+
import { eachNoteReference } from "./references.js";
|
|
6
8
|
var NOTE_CONTENT_TYPES = {
|
|
7
9
|
footnote: "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml",
|
|
8
10
|
endnote: "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml"
|
|
@@ -60,12 +62,9 @@ function frozenNotes(session, kind) {
|
|
|
60
62
|
}
|
|
61
63
|
function referenceIds(doc, kind) {
|
|
62
64
|
const ids = /* @__PURE__ */ new Set();
|
|
63
|
-
doc
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
-
ids.add(id);
|
|
67
|
-
}
|
|
68
|
-
return true;
|
|
65
|
+
eachNoteReference(doc, ({ key }) => {
|
|
66
|
+
const note = key === null ? null : noteKeyOf(key);
|
|
67
|
+
if (note?.kind === kind) ids.add(note.id);
|
|
69
68
|
});
|
|
70
69
|
return ids;
|
|
71
70
|
}
|
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* the same way, from here.
|
|
8
8
|
*/
|
|
9
9
|
import type { Node as PMNode } from "prosemirror-model";
|
|
10
|
+
import { type ExportProblemReason, type ExportProblemStory } from "../ooxml/errors";
|
|
10
11
|
import type { ExportRefs } from "./exportRefs";
|
|
11
12
|
import { type SessionStore } from "./session";
|
|
12
13
|
/** Why there is no original to write, which a block that came in from another document answers differently */
|
|
13
|
-
export declare function lostOriginal(node: PMNode, session: SessionStore | null):
|
|
14
|
+
export declare function lostOriginal(node: PMNode, session: SessionStore | null, story: ExportProblemStory | null): {
|
|
15
|
+
readonly message: string;
|
|
16
|
+
readonly reason: ExportProblemReason;
|
|
17
|
+
};
|
|
14
18
|
export declare function serializePreservedBlock(node: PMNode, refs: ExportRefs): string;
|
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
// src/docx/serializePreserved.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DocxExportError
|
|
4
|
+
} from "../ooxml/errors.js";
|
|
3
5
|
import { originalBlock, splitBlockKey } from "./session.js";
|
|
4
|
-
function lostOriginal(node, session) {
|
|
6
|
+
function lostOriginal(node, session, story) {
|
|
5
7
|
const srcId = node.attrs.srcId;
|
|
6
8
|
const key = typeof srcId === "string" ? splitBlockKey(srcId) : null;
|
|
7
|
-
|
|
9
|
+
const name = node.type.name;
|
|
10
|
+
if (key === null || key.sessionId === session?.sessionId) {
|
|
11
|
+
return {
|
|
12
|
+
message: "a preserved block has lost its original XML",
|
|
13
|
+
reason: { kind: "lost-preserved-xml", node: name, story }
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
message: `a preserved block comes from another document (${key.sessionId})`,
|
|
18
|
+
reason: {
|
|
19
|
+
kind: "preserved-from-another-document",
|
|
20
|
+
node: name,
|
|
21
|
+
sessionId: key.sessionId,
|
|
22
|
+
story
|
|
23
|
+
}
|
|
24
|
+
};
|
|
8
25
|
}
|
|
9
26
|
function serializePreservedBlock(node, refs) {
|
|
10
27
|
const xml = node.attrs.xml;
|
|
11
28
|
if (typeof xml === "string") return xml;
|
|
12
29
|
const imported = refs.session ? originalBlock(node, refs.session) : void 0;
|
|
13
30
|
if (!imported) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
);
|
|
31
|
+
const { message, reason } = lostOriginal(node, refs.session, null);
|
|
32
|
+
throw new DocxExportError("lost-original", message, {
|
|
33
|
+
problem: { code: "lost-original", message, reason }
|
|
34
|
+
});
|
|
18
35
|
}
|
|
19
36
|
return imported.xml;
|
|
20
37
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* and the export invariants would have a third answer of their own.
|
|
7
7
|
*/
|
|
8
8
|
import type { Node as PMNode } from "prosemirror-model";
|
|
9
|
-
import type { DocxExportErrorCode } from "../ooxml/errors";
|
|
9
|
+
import type { DocxExportErrorCode, ExportPartName, ExportProblemReason } from "../ooxml/errors";
|
|
10
10
|
import { type StoryKey, type StoryKind } from "../schema/stories";
|
|
11
11
|
import { type StoryToSettle } from "./identities";
|
|
12
12
|
import type { PartPlanner } from "./partPlan";
|
|
@@ -27,8 +27,6 @@ export type StoryChange = {
|
|
|
27
27
|
readonly key: StoryKey;
|
|
28
28
|
readonly current: PMNode;
|
|
29
29
|
};
|
|
30
|
-
/** The id a key of this kind names */
|
|
31
|
-
export declare function storyIdOf(key: StoryKey, kind: StoryKind): string;
|
|
32
30
|
/** Every story of this kind the package arrived with or the document now holds, in part order then id order */
|
|
33
31
|
export declare function storyChangesOf(doc: PMNode, session: SessionStore, kind: StoryKind): readonly StoryChange[];
|
|
34
32
|
export type StoryChanged = Exclude<StoryChange["change"], "kept">;
|
|
@@ -47,6 +45,11 @@ export interface StoryWriting {
|
|
|
47
45
|
export declare function storyEntriesWriting(part: StoryEntriesPart): StoryWriting;
|
|
48
46
|
/** A writer that takes these changes to every story of the kind, freezing none of them */
|
|
49
47
|
export declare function storyWriting(kind: StoryKind, changes: readonly StoryChanged[]): StoryWriting;
|
|
48
|
+
/** A change to a side story that no part writer carries into the file */
|
|
49
|
+
export interface UnwrittenStoryChange {
|
|
50
|
+
readonly key: StoryKey;
|
|
51
|
+
readonly change: StoryChanged;
|
|
52
|
+
}
|
|
50
53
|
/**
|
|
51
54
|
* Every change the document made to a side story that no part writer carries into the file, as the
|
|
52
55
|
* key it stands under and what was done to it.
|
|
@@ -56,13 +59,26 @@ export declare function storyWriting(kind: StoryKind, changes: readonly StoryCha
|
|
|
56
59
|
* writer does not write - and a change to an entry written as it arrived are all reported here
|
|
57
60
|
* rather than dropped from the file without a word.
|
|
58
61
|
*/
|
|
59
|
-
export declare function unwrittenStoryChanges(writings: readonly StoryWriting[], doc: PMNode, session: SessionStore): readonly
|
|
62
|
+
export declare function unwrittenStoryChanges(writings: readonly StoryWriting[], doc: PMNode, session: SessionStore): readonly UnwrittenStoryChange[];
|
|
63
|
+
/** One side story the export writes block by block, under the key it stands at */
|
|
64
|
+
export interface RewrittenStory {
|
|
60
65
|
readonly key: StoryKey;
|
|
61
|
-
readonly
|
|
62
|
-
}
|
|
66
|
+
readonly story: PMNode;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Every side story the export writes block by block, which is every one a part writer carries an
|
|
70
|
+
* addition or an edit of.
|
|
71
|
+
*
|
|
72
|
+
* A story the writer hands back as the bytes it arrived as - one nobody touched, one its part
|
|
73
|
+
* freezes, one whose change no writer carries, which `unwrittenStoryChanges` refuses instead -
|
|
74
|
+
* goes out holding exactly what it came in holding, so nothing about it can newly be refused. The
|
|
75
|
+
* same "changed and written" answer decides both, so a writer and the invariants cannot disagree
|
|
76
|
+
* about which story is written from a node.
|
|
77
|
+
*/
|
|
78
|
+
export declare function rewrittenStories(writings: readonly StoryWriting[], doc: PMNode, session: SessionStore): readonly RewrittenStory[];
|
|
63
79
|
/** A part holding one entry per story, e.g. `w:footnotes` holding a `w:footnote` apiece */
|
|
64
80
|
export interface StoryEntriesPart {
|
|
65
|
-
readonly name:
|
|
81
|
+
readonly name: ExportPartName;
|
|
66
82
|
readonly kind: StoryKind;
|
|
67
83
|
readonly relType: string;
|
|
68
84
|
readonly contentType: string;
|
|
@@ -83,16 +99,21 @@ export interface StoryEntriesPart {
|
|
|
83
99
|
/** The entries a part this planner creates opens with, before any story */
|
|
84
100
|
prelude(taken: ReadonlySet<string>): string;
|
|
85
101
|
}
|
|
102
|
+
/** One story a part writes, beside the key it stands under */
|
|
103
|
+
export interface StoryEntry extends StoryToSettle {
|
|
104
|
+
readonly key: StoryKey;
|
|
105
|
+
}
|
|
86
106
|
/**
|
|
87
107
|
* The stories a part is written with, in the order it writes them, and none where no story of its
|
|
88
108
|
* kind changed and the part is not written at all. The identity pass runs over this list as one
|
|
89
109
|
* part, and the export invariants ask the same list.
|
|
90
110
|
*/
|
|
91
|
-
export declare function storyEntriesOf(part: StoryEntriesPart, doc: PMNode, session: SessionStore): readonly
|
|
111
|
+
export declare function storyEntriesOf(part: StoryEntriesPart, doc: PMNode, session: SessionStore): readonly StoryEntry[];
|
|
92
112
|
/** One reason the part cannot take the changes its stories went through */
|
|
93
113
|
export interface StoryPartProblem {
|
|
94
114
|
readonly code: DocxExportErrorCode;
|
|
95
115
|
readonly message: string;
|
|
116
|
+
readonly reason: ExportProblemReason;
|
|
96
117
|
}
|
|
97
118
|
/**
|
|
98
119
|
* Why the part cannot take what the document did to its stories, or none when it can. The part the
|
package/dist/docx/storyParts.js
CHANGED
|
@@ -19,6 +19,7 @@ import { sameSource } from "../schema/sourceEquality.js";
|
|
|
19
19
|
import {
|
|
20
20
|
asStoryKey,
|
|
21
21
|
STORY_KINDS,
|
|
22
|
+
splitStoryKey,
|
|
22
23
|
storiesOf,
|
|
23
24
|
storyKey,
|
|
24
25
|
storyNodeOf
|
|
@@ -40,9 +41,6 @@ function byId(a, b) {
|
|
|
40
41
|
if (a === b) return 0;
|
|
41
42
|
return a < b ? -1 : 1;
|
|
42
43
|
}
|
|
43
|
-
function storyIdOf(key, kind) {
|
|
44
|
-
return key.slice(kind.length + 1);
|
|
45
|
-
}
|
|
46
44
|
function arrivedChange(doc, imported) {
|
|
47
45
|
const current = storyNodeOf(doc, imported.key);
|
|
48
46
|
if (current === null) return { change: "removed", imported };
|
|
@@ -53,7 +51,7 @@ function storyChangesOf(doc, session, kind) {
|
|
|
53
51
|
const added = Object.keys(storiesOf(doc)).flatMap((text) => {
|
|
54
52
|
const key = asStoryKey(text);
|
|
55
53
|
return key === null || !key.startsWith(`${kind}:`) || session.stories.has(key) ? [] : [key];
|
|
56
|
-
}).sort((a, b) => byId(
|
|
54
|
+
}).sort((a, b) => byId(splitStoryKey(a).id, splitStoryKey(b).id)).flatMap((key) => {
|
|
57
55
|
const current = storyNodeOf(doc, key);
|
|
58
56
|
return current === null ? [] : [{ change: "added", key, current }];
|
|
59
57
|
});
|
|
@@ -91,6 +89,21 @@ function unwrittenStoryChanges(writings, doc, session) {
|
|
|
91
89
|
});
|
|
92
90
|
});
|
|
93
91
|
}
|
|
92
|
+
function rewrittenStories(writings, doc, session) {
|
|
93
|
+
const byKind = new Map(writings.map((writing) => [writing.kind, writing]));
|
|
94
|
+
return STORY_KINDS.flatMap((kind) => {
|
|
95
|
+
const writing = byKind.get(kind);
|
|
96
|
+
if (writing === void 0) return [];
|
|
97
|
+
const frozen = writing.frozenEntries(session);
|
|
98
|
+
return storyChangesOf(doc, session, kind).flatMap(
|
|
99
|
+
(entry) => {
|
|
100
|
+
if (entry.change !== "added" && entry.change !== "edited") return [];
|
|
101
|
+
const key = entry.change === "added" ? entry.key : entry.imported.key;
|
|
102
|
+
return writing.changes.includes(entry.change) && !frozen.has(key) ? [{ key, story: entry.current }] : [];
|
|
103
|
+
}
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
94
107
|
function writtenStoryOf(change, frozen) {
|
|
95
108
|
if (change.change === "added") return change.current;
|
|
96
109
|
if (change.change === "kept" || frozen.has(change.imported.key)) {
|
|
@@ -102,11 +115,13 @@ function writtenStories(changes, frozen) {
|
|
|
102
115
|
return changes.flatMap((change) => {
|
|
103
116
|
const story = writtenStoryOf(change, frozen);
|
|
104
117
|
if (story === null) return [];
|
|
118
|
+
const key = change.change === "added" ? change.key : change.imported.key;
|
|
105
119
|
return [
|
|
106
120
|
{
|
|
107
121
|
change,
|
|
122
|
+
key,
|
|
108
123
|
story,
|
|
109
|
-
frozen: change.change !== "added" && frozen.has(
|
|
124
|
+
frozen: change.change !== "added" && frozen.has(key)
|
|
110
125
|
}
|
|
111
126
|
];
|
|
112
127
|
});
|
|
@@ -116,14 +131,17 @@ function storyEntriesOf(part, doc, session) {
|
|
|
116
131
|
return changed(changes) ? writtenStories(changes, part.frozenEntries(session)) : [];
|
|
117
132
|
}
|
|
118
133
|
function addedIdProblems(part, changes) {
|
|
119
|
-
return changes.flatMap(
|
|
120
|
-
(change
|
|
134
|
+
return changes.flatMap((change) => {
|
|
135
|
+
if (change.change !== "added") return [];
|
|
136
|
+
const story = splitStoryKey(change.key);
|
|
137
|
+
return ST_DecimalNumber.parse(story.id) === null ? [
|
|
121
138
|
{
|
|
122
139
|
code: "unsupported-content",
|
|
123
|
-
message: `the ${change.key} story is named by no whole number, and a ${wName(part.entry)} is identified by one
|
|
140
|
+
message: `the ${change.key} story is named by no whole number, and a ${wName(part.entry)} is identified by one`,
|
|
141
|
+
reason: { kind: "story-id-not-a-number", story }
|
|
124
142
|
}
|
|
125
|
-
] : []
|
|
126
|
-
);
|
|
143
|
+
] : [];
|
|
144
|
+
});
|
|
127
145
|
}
|
|
128
146
|
function storyEntriesProblems(part, doc, session) {
|
|
129
147
|
const changes = storyChangesOf(doc, session, part.kind);
|
|
@@ -136,12 +154,19 @@ function storyEntriesProblems(part, doc, session) {
|
|
|
136
154
|
);
|
|
137
155
|
if (xml !== null) {
|
|
138
156
|
const problem = partRootProblem(xml, part.root);
|
|
139
|
-
return problem === null ? [] : [
|
|
157
|
+
return problem === null ? [] : [
|
|
158
|
+
{
|
|
159
|
+
code: "malformed-xml",
|
|
160
|
+
message: problem,
|
|
161
|
+
reason: { kind: "unwritable-part-root", part: part.name }
|
|
162
|
+
}
|
|
163
|
+
];
|
|
140
164
|
}
|
|
141
165
|
return session.parts.has(CONTENT_TYPES_PATH) ? [] : [
|
|
142
166
|
{
|
|
143
167
|
code: "missing-content-types",
|
|
144
|
-
message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}
|
|
168
|
+
message: `cannot add a part to a package that has no ${CONTENT_TYPES_PATH}`,
|
|
169
|
+
reason: { kind: "missing-content-types", part: part.name }
|
|
145
170
|
}
|
|
146
171
|
];
|
|
147
172
|
}
|
|
@@ -159,7 +184,7 @@ function entryXml(part, { change, frozen }, settled, refs) {
|
|
|
159
184
|
return serializeStory(
|
|
160
185
|
settled,
|
|
161
186
|
null,
|
|
162
|
-
containerOf(part,
|
|
187
|
+
containerOf(part, splitStoryKey(change.key).id),
|
|
163
188
|
refs
|
|
164
189
|
);
|
|
165
190
|
}
|
|
@@ -256,7 +281,7 @@ function planEntries(part, doc, session, context) {
|
|
|
256
281
|
context.contentTypes.addOverride(path, part.contentType);
|
|
257
282
|
const taken = /* @__PURE__ */ new Set([
|
|
258
283
|
...changes.map(
|
|
259
|
-
(change) => change.change === "added" ?
|
|
284
|
+
(change) => change.change === "added" ? splitStoryKey(change.key).id : change.imported.id
|
|
260
285
|
),
|
|
261
286
|
...part.referencedIds(doc)
|
|
262
287
|
]);
|
|
@@ -278,12 +303,12 @@ function storyEntriesPlanner(part) {
|
|
|
278
303
|
}
|
|
279
304
|
export {
|
|
280
305
|
EVERY_STORY_CHANGE,
|
|
306
|
+
rewrittenStories,
|
|
281
307
|
storyChangesOf,
|
|
282
308
|
storyEntriesOf,
|
|
283
309
|
storyEntriesPlanner,
|
|
284
310
|
storyEntriesProblems,
|
|
285
311
|
storyEntriesWriting,
|
|
286
|
-
storyIdOf,
|
|
287
312
|
storyWriting,
|
|
288
313
|
unwrittenStoryChanges
|
|
289
314
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Document-level readers for whether the document as it stands can be written back. */
|
|
2
2
|
import type { EditorState } from "prosemirror-state";
|
|
3
3
|
import { type ExportProblem } from "../../docx/invariants";
|
|
4
|
-
export type { ExportProblem } from "../../docx/invariants";
|
|
4
|
+
export type { ExportPartName, ExportProblem, ExportProblemReason, ExportProblemStory, ExportStoryKind, } from "../../docx/invariants";
|
|
5
5
|
/**
|
|
6
6
|
* Every reason writing the document back would be refused, in the order `exportDocx` would raise
|
|
7
|
-
* them, each with the code and message the refusal would carry
|
|
8
|
-
* where there is one. Empty when the file would be
|
|
7
|
+
* them, each with the code and message the refusal would carry, the `reason` naming what it is
|
|
8
|
+
* about, and the position in the document where there is one. Empty when the file would be
|
|
9
|
+
* written.
|
|
9
10
|
*
|
|
10
11
|
* A state built without an opened document has no file to write back into, so nothing about it
|
|
11
12
|
* can be refused and it reports no problem.
|
|
@@ -63,7 +63,7 @@ export { addComment, addCommentReply, canAddComment, canEditComment, documentCom
|
|
|
63
63
|
* with the code `exportDocx` would throw it under. `canExport` is what an export control is
|
|
64
64
|
* drawn from, and `downloadDocx` answers `blocked` with the same list.
|
|
65
65
|
*/
|
|
66
|
-
export type { ExportProblem } from "./exportQueries";
|
|
66
|
+
export type { ExportPartName, ExportProblem, ExportProblemReason, ExportProblemStory, ExportStoryKind, } from "./exportQueries";
|
|
67
67
|
export { canExport, documentExportProblems } from "./exportQueries";
|
|
68
68
|
export type { FidelityCode, FidelityNote, FidelitySeverity, } from "./fidelityQueries";
|
|
69
69
|
export { documentFidelity } from "./fidelityQueries";
|
package/dist/index.d.ts
CHANGED
|
@@ -16,12 +16,12 @@ export type { DocxEditorHandle, DocxEditorMode, DocxEditorProps, } from "./DocxE
|
|
|
16
16
|
export { DocxEditor } from "./DocxEditor";
|
|
17
17
|
export type { DocxBytes, DocxSource } from "./docx/importDocx";
|
|
18
18
|
/** What the handle and a `blocked` download report: each reason the document cannot be written, under the code `DocxExportError` would throw it with */
|
|
19
|
-
export type { ExportProblem } from "./docx/invariants";
|
|
19
|
+
export type { ExportPartName, ExportProblem, ExportProblemReason, ExportProblemStory, ExportStoryKind, } from "./docx/invariants";
|
|
20
20
|
export type { DownloadDocxOptions, DownloadDocxResult, } from "./download";
|
|
21
21
|
export { downloadDocx } from "./download";
|
|
22
22
|
export type { CommentAuthor } from "./editor/commands/commentCommands";
|
|
23
23
|
export type { CellFormat, CellVerticalAlign, DocumentDefaults, HighlightName, LineSpacing, NumberingRef, ParagraphAlign, ParagraphFormat, RowFormat, RowHeight, RunFormat, TableFormat, TableWidth, TableWidthType, UnderlineKind, VerticalAlign, } from "./model/format";
|
|
24
|
-
export type { DocxExportErrorCode, DocxImportErrorCode, } from "./ooxml/errors";
|
|
24
|
+
export type { DocxExportErrorCode, DocxExportErrorOptions, DocxImportErrorCode, } from "./ooxml/errors";
|
|
25
25
|
export { DocxExportError, DocxImportError } from "./ooxml/errors";
|
|
26
26
|
export { docxSchema } from "./schema";
|
|
27
27
|
/** Whose comments the panel offers to edit, which `DocxEditorMode` takes */
|
package/dist/ooxml/errors.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Messages are English one-liners meant for developers. The `code` is the stable,
|
|
5
5
|
* machine-readable part: consumers switch on it to show their own localized text,
|
|
6
|
-
* so a code never changes meaning even when a message is reworded.
|
|
6
|
+
* so a code never changes meaning even when a message is reworded. An export refusal carries an
|
|
7
|
+
* `ExportProblemReason` besides, which tells the several situations one code covers apart and
|
|
8
|
+
* names what each is about, so a host can say which content to put back.
|
|
7
9
|
*/
|
|
8
10
|
/**
|
|
9
11
|
* Why a document could not be opened.
|
|
@@ -33,8 +35,139 @@ export declare class DocxImportError extends Error {
|
|
|
33
35
|
* - `invalid-table`: the table grid is inconsistent, e.g. a vertical merge outliving its rows
|
|
34
36
|
*/
|
|
35
37
|
export type DocxExportErrorCode = "missing-content-types" | "unsupported-content" | "lost-original" | "malformed-xml" | "invalid-table";
|
|
38
|
+
/**
|
|
39
|
+
* The kinds of side story a refusal can be about, which are the kinds a document holds beside its
|
|
40
|
+
* body (`schema/stories`). Spelled here because the reasons stand below the schema layer.
|
|
41
|
+
*/
|
|
42
|
+
export type ExportStoryKind = "comment" | "footnote" | "endnote" | "header" | "footer";
|
|
43
|
+
/**
|
|
44
|
+
* One side story, as the document holds it.
|
|
45
|
+
*
|
|
46
|
+
* A reason names it wherever the content the refusal is about may stand outside the body, and the
|
|
47
|
+
* refusal is then about that story rather than the document: `story` is null for the body itself.
|
|
48
|
+
*/
|
|
49
|
+
export interface ExportProblemStory {
|
|
50
|
+
readonly kind: ExportStoryKind;
|
|
51
|
+
/** A note's number, a comment's id, or the path of the part a header or footer stands in */
|
|
52
|
+
readonly id: string;
|
|
53
|
+
}
|
|
54
|
+
/** A part the export writes beside the body, named after what it holds */
|
|
55
|
+
export type ExportPartName = "media" | "numbering" | "comments" | "commentsExtended" | "people" | "footnotes" | "endnotes";
|
|
56
|
+
/**
|
|
57
|
+
* What a refusal is actually about.
|
|
58
|
+
*
|
|
59
|
+
* A `code` groups several situations - `unsupported-content` alone covers a list nothing defines,
|
|
60
|
+
* a block standing twice, and a change to a story no writer carries - and the `message` telling
|
|
61
|
+
* them apart is an English one-liner for a developer. This says which situation was met and names
|
|
62
|
+
* what it is about, so a host can write the sentence its own user reads. Switch on `kind`.
|
|
63
|
+
*/
|
|
64
|
+
export type ExportProblemReason =
|
|
65
|
+
/** A preserved fragment holding bookmark markup could not be parsed (`malformed-xml`) */
|
|
66
|
+
{
|
|
67
|
+
readonly kind: "unreadable-preserved-xml";
|
|
68
|
+
readonly story: ExportProblemStory | null;
|
|
69
|
+
}
|
|
70
|
+
/** A bookmark marker carries no id to pair it by (`malformed-xml`) */
|
|
71
|
+
| {
|
|
72
|
+
readonly kind: "unnamed-bookmark";
|
|
73
|
+
readonly marker: "start" | "end";
|
|
74
|
+
readonly story: ExportProblemStory | null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A bookmark marker whose partner is gone (`malformed-xml`): a `start` with no end after it, or
|
|
78
|
+
* an `end` with no start before it.
|
|
79
|
+
*/
|
|
80
|
+
| {
|
|
81
|
+
readonly kind: "unmatched-bookmark";
|
|
82
|
+
readonly id: string;
|
|
83
|
+
readonly marker: "start" | "end";
|
|
84
|
+
readonly story: ExportProblemStory | null;
|
|
85
|
+
}
|
|
86
|
+
/** One bookmark id is started twice (`malformed-xml`) */
|
|
87
|
+
| {
|
|
88
|
+
readonly kind: "repeated-bookmark-start";
|
|
89
|
+
readonly id: string;
|
|
90
|
+
readonly story: ExportProblemStory | null;
|
|
91
|
+
}
|
|
92
|
+
/** A part cannot be rewritten around its root element (`malformed-xml`) */
|
|
93
|
+
| {
|
|
94
|
+
readonly kind: "unwritable-part-root";
|
|
95
|
+
readonly part: ExportPartName;
|
|
96
|
+
}
|
|
97
|
+
/** A cell's vertical merge covers rows its table does not have (`invalid-table`) */
|
|
98
|
+
| {
|
|
99
|
+
readonly kind: "vertical-merge-past-table";
|
|
100
|
+
readonly story: ExportProblemStory | null;
|
|
101
|
+
}
|
|
102
|
+
/** A node that is written from its original XML alone no longer holds it (`lost-original`) */
|
|
103
|
+
| {
|
|
104
|
+
readonly kind: "lost-preserved-xml";
|
|
105
|
+
readonly node: string;
|
|
106
|
+
readonly story: ExportProblemStory | null;
|
|
107
|
+
}
|
|
108
|
+
/** A placeholder pasted in from another opened document, whose XML this session never read (`lost-original`) */
|
|
109
|
+
| {
|
|
110
|
+
readonly kind: "preserved-from-another-document";
|
|
111
|
+
readonly node: string;
|
|
112
|
+
/** The session the placeholder was opened in */
|
|
113
|
+
readonly sessionId: string;
|
|
114
|
+
readonly story: ExportProblemStory | null;
|
|
115
|
+
}
|
|
116
|
+
/** One preserved block stands in two places, and it has one original XML to be written (`unsupported-content`) */
|
|
117
|
+
| {
|
|
118
|
+
readonly kind: "duplicate-preserved-block";
|
|
119
|
+
readonly node: string;
|
|
120
|
+
readonly story: ExportProblemStory | null;
|
|
121
|
+
}
|
|
122
|
+
/** A paragraph is in a list neither the file nor the editor's register defines (`unsupported-content`) */
|
|
123
|
+
| {
|
|
124
|
+
readonly kind: "undefined-list";
|
|
125
|
+
readonly numId: number;
|
|
126
|
+
readonly story: ExportProblemStory | null;
|
|
127
|
+
}
|
|
128
|
+
/** A story was changed and no part writer carries that change into the file (`unsupported-content`) */
|
|
129
|
+
| {
|
|
130
|
+
readonly kind: "unwritten-story-change";
|
|
131
|
+
readonly story: ExportProblemStory;
|
|
132
|
+
readonly change: "added" | "edited" | "removed";
|
|
133
|
+
}
|
|
134
|
+
/** A story was added under an id its part identifies entries by no whole number of (`unsupported-content`) */
|
|
135
|
+
| {
|
|
136
|
+
readonly kind: "story-id-not-a-number";
|
|
137
|
+
readonly story: ExportProblemStory;
|
|
138
|
+
}
|
|
139
|
+
/** A part the writer has to add cannot be declared, the package having no content types part (`missing-content-types`) */
|
|
140
|
+
| {
|
|
141
|
+
readonly kind: "missing-content-types";
|
|
142
|
+
readonly part: ExportPartName;
|
|
143
|
+
};
|
|
144
|
+
/** One reason the document cannot be written back, with the code `exportDocx` would throw it under */
|
|
145
|
+
export interface ExportProblem {
|
|
146
|
+
readonly code: DocxExportErrorCode;
|
|
147
|
+
/** An English one-liner for a developer; reworded freely. Read `code` and `reason` instead */
|
|
148
|
+
readonly message: string;
|
|
149
|
+
/** Which of the situations the code covers was met, and what it is about */
|
|
150
|
+
readonly reason: ExportProblemReason;
|
|
151
|
+
/**
|
|
152
|
+
* Where the problem stands in the document: the block or marker it is about, or, for a footnote
|
|
153
|
+
* or an endnote, the first reference to that note in the body, the note's own text standing
|
|
154
|
+
* nowhere there. Absent for a problem of the package, of the session, of a header, footer or
|
|
155
|
+
* comment story, and of a note the body refers to nowhere.
|
|
156
|
+
*/
|
|
157
|
+
readonly pos?: number;
|
|
158
|
+
}
|
|
159
|
+
/** What a `DocxExportError` may be given beyond its code and message */
|
|
160
|
+
export interface DocxExportErrorOptions extends ErrorOptions {
|
|
161
|
+
/** The entry `exportProblems` reports for this refusal, where an invariant predicted it */
|
|
162
|
+
readonly problem?: ExportProblem;
|
|
163
|
+
}
|
|
36
164
|
/** Thrown when an edited document cannot be written back out without losing or corrupting content */
|
|
37
165
|
export declare class DocxExportError extends Error {
|
|
38
166
|
readonly code: DocxExportErrorCode;
|
|
39
|
-
|
|
167
|
+
/**
|
|
168
|
+
* What `exportProblems` reports for this refusal, carrying its `reason` and `pos`. Absent for a
|
|
169
|
+
* refusal met while writing that no invariant predicted.
|
|
170
|
+
*/
|
|
171
|
+
readonly problem?: ExportProblem;
|
|
172
|
+
constructor(code: DocxExportErrorCode, message: string, options?: DocxExportErrorOptions);
|
|
40
173
|
}
|
package/dist/ooxml/errors.js
CHANGED
|
@@ -9,10 +9,16 @@ var DocxImportError = class extends Error {
|
|
|
9
9
|
};
|
|
10
10
|
var DocxExportError = class extends Error {
|
|
11
11
|
code;
|
|
12
|
+
/**
|
|
13
|
+
* What `exportProblems` reports for this refusal, carrying its `reason` and `pos`. Absent for a
|
|
14
|
+
* refusal met while writing that no invariant predicted.
|
|
15
|
+
*/
|
|
16
|
+
problem;
|
|
12
17
|
constructor(code, message, options) {
|
|
13
18
|
super(message, options);
|
|
14
19
|
this.name = "DocxExportError";
|
|
15
20
|
this.code = code;
|
|
21
|
+
this.problem = options?.problem;
|
|
16
22
|
}
|
|
17
23
|
};
|
|
18
24
|
export {
|
package/dist/schema/stories.d.ts
CHANGED
|
@@ -35,12 +35,20 @@ export declare const STORIES_ATTR = "stories";
|
|
|
35
35
|
export declare function storyKey<K extends StoryKind>(kind: K, id: string): `${K}:${string}`;
|
|
36
36
|
/** The key of a story that is a note: `footnote:2`, `endnote:3` */
|
|
37
37
|
export type NoteKey = `${NoteKind}:${string}`;
|
|
38
|
+
/** What a key names: which kind of story it is, and the id it stands under */
|
|
39
|
+
export interface StoryName {
|
|
40
|
+
readonly kind: StoryKind;
|
|
41
|
+
readonly id: string;
|
|
42
|
+
}
|
|
38
43
|
/**
|
|
39
|
-
* The
|
|
44
|
+
* The kind and the id a key names.
|
|
40
45
|
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
46
|
+
* An id carries colons of its own - a header is named by its part path - so the kind is everything
|
|
47
|
+
* up to the first one. A key is spelled `${StoryKind}:${string}`, so the kind always reads back;
|
|
48
|
+
* text that may spell no kind at all comes through `asStoryKey` first.
|
|
43
49
|
*/
|
|
50
|
+
export declare function splitStoryKey(key: StoryKey): StoryName;
|
|
51
|
+
/** The note this key names, and null for a key naming a story of another kind */
|
|
44
52
|
export declare function noteKeyOf(key: StoryKey): {
|
|
45
53
|
readonly kind: NoteKind;
|
|
46
54
|
readonly id: string;
|
package/dist/schema/stories.js
CHANGED
|
@@ -15,11 +15,16 @@ var STORIES_ATTR = "stories";
|
|
|
15
15
|
function storyKey(kind, id) {
|
|
16
16
|
return `${kind}:${id}`;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
18
|
+
function splitStoryKey(key) {
|
|
19
19
|
const at = key.indexOf(":");
|
|
20
20
|
const named = key.slice(0, at);
|
|
21
|
-
const kind =
|
|
22
|
-
return kind
|
|
21
|
+
const kind = STORY_KINDS.find((candidate) => candidate === named);
|
|
22
|
+
return { kind: kind ?? "comment", id: key.slice(at + 1) };
|
|
23
|
+
}
|
|
24
|
+
function noteKeyOf(key) {
|
|
25
|
+
const { kind, id } = splitStoryKey(key);
|
|
26
|
+
const note = NOTE_KINDS.find((candidate) => candidate === kind);
|
|
27
|
+
return note === void 0 ? null : { kind: note, id };
|
|
23
28
|
}
|
|
24
29
|
var EDITABLE_NOTE_KINDS = NOTE_KINDS;
|
|
25
30
|
function asStoryKey(text) {
|
|
@@ -88,6 +93,7 @@ export {
|
|
|
88
93
|
noteKeyOf,
|
|
89
94
|
noteName,
|
|
90
95
|
sameStory,
|
|
96
|
+
splitStoryKey,
|
|
91
97
|
storiesOf,
|
|
92
98
|
storyKey,
|
|
93
99
|
storyNodeOf,
|