@witchcraft/editor 0.0.8 → 0.1.0

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.
Files changed (100) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +2 -2
  3. package/dist/runtime/components/CodeBlockThemePicker.d.vue.ts +4 -2
  4. package/dist/runtime/components/CodeBlockThemePicker.vue +0 -1
  5. package/dist/runtime/components/CodeBlockThemePicker.vue.d.ts +4 -2
  6. package/dist/runtime/components/Commands.d.vue.ts +2 -1
  7. package/dist/runtime/components/Commands.vue.d.ts +2 -1
  8. package/dist/runtime/components/Editor.d.vue.ts +3 -12
  9. package/dist/runtime/components/Editor.vue.d.ts +3 -12
  10. package/dist/runtime/components/EditorDemoApp.d.vue.ts +2 -1
  11. package/dist/runtime/components/EditorDemoApp.vue.d.ts +2 -1
  12. package/dist/runtime/components/EditorDemoControls.d.vue.ts +4 -2
  13. package/dist/runtime/components/EditorDemoControls.vue.d.ts +4 -2
  14. package/dist/runtime/components/TestWrapper.d.vue.ts +2 -1
  15. package/dist/runtime/components/TestWrapper.vue.d.ts +2 -1
  16. package/dist/runtime/demo/App.d.vue.ts +2 -1
  17. package/dist/runtime/demo/App.vue.d.ts +2 -1
  18. package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.d.vue.ts +2 -1
  19. package/dist/runtime/pm/features/Blocks/components/DragTreeHandle.vue.d.ts +2 -1
  20. package/dist/runtime/pm/features/Blocks/components/ItemMenu.d.vue.ts +2 -1
  21. package/dist/runtime/pm/features/Blocks/components/ItemMenu.vue.d.ts +2 -1
  22. package/dist/runtime/pm/features/Blocks/components/ItemNodeView.d.vue.ts +2 -1
  23. package/dist/runtime/pm/features/Blocks/components/ItemNodeView.vue.d.ts +2 -1
  24. package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.d.vue.ts +2 -1
  25. package/dist/runtime/pm/features/CodeBlock/components/CodeBlockView.vue.d.ts +2 -1
  26. package/dist/runtime/pm/features/CommandsMenus/components/CommandBar.d.vue.ts +2 -1
  27. package/dist/runtime/pm/features/CommandsMenus/components/CommandBar.vue.d.ts +2 -1
  28. package/dist/runtime/pm/features/CommandsMenus/components/CommandBarItem.d.vue.ts +2 -1
  29. package/dist/runtime/pm/features/CommandsMenus/components/CommandBarItem.vue.d.ts +2 -1
  30. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.d.vue.ts +2 -1
  31. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuGroup.vue.d.ts +2 -1
  32. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuItem.d.vue.ts +2 -1
  33. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuItem.vue.d.ts +2 -1
  34. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuList.d.vue.ts +2 -1
  35. package/dist/runtime/pm/features/CommandsMenus/components/CommandMenuList.vue.d.ts +2 -1
  36. package/dist/runtime/pm/features/CommandsMenus/components/TextIcon.d.vue.ts +2 -1
  37. package/dist/runtime/pm/features/CommandsMenus/components/TextIcon.vue.d.ts +2 -1
  38. package/dist/runtime/pm/features/CommandsMenus/icons/HighlightIcon.d.vue.ts +2 -1
  39. package/dist/runtime/pm/features/CommandsMenus/icons/HighlightIcon.vue.d.ts +2 -1
  40. package/dist/runtime/pm/features/CommandsMenus/icons/SubscriptIcon.d.vue.ts +2 -1
  41. package/dist/runtime/pm/features/CommandsMenus/icons/SubscriptIcon.vue.d.ts +2 -1
  42. package/dist/runtime/pm/features/CommandsMenus/icons/SuperscriptIcon.d.vue.ts +2 -1
  43. package/dist/runtime/pm/features/CommandsMenus/icons/SuperscriptIcon.vue.d.ts +2 -1
  44. package/dist/runtime/pm/features/DocumentApi/DocumentApi.d.ts +3 -0
  45. package/dist/runtime/pm/features/DocumentApi/DocumentApi.js +2 -0
  46. package/dist/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.js +4 -1
  47. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.d.vue.ts +2 -1
  48. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +3 -3
  49. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue.d.ts +2 -1
  50. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.d.vue.ts +2 -1
  51. package/dist/runtime/pm/features/EmbeddedDocument/components/EmbeddedNodeView.vue.d.ts +2 -1
  52. package/dist/runtime/pm/features/FileLoader/components/FileLoaderNodeView.d.vue.ts +2 -1
  53. package/dist/runtime/pm/features/FileLoader/components/FileLoaderNodeView.vue.d.ts +2 -1
  54. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.d.vue.ts +2 -1
  55. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.vue +0 -1
  56. package/dist/runtime/pm/features/Iframe/components/IframeNodeView.vue.d.ts +2 -1
  57. package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.d.vue.ts +4 -2
  58. package/dist/runtime/pm/features/Link/components/BubbleMenuExternalLink.vue.d.ts +4 -2
  59. package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.d.vue.ts +4 -2
  60. package/dist/runtime/pm/features/Link/components/BubbleMenuInternalLink.vue.d.ts +4 -2
  61. package/dist/runtime/pm/features/Link/components/BubbleMenuLink.d.vue.ts +2 -1
  62. package/dist/runtime/pm/features/Link/components/BubbleMenuLink.vue.d.ts +2 -1
  63. package/dist/runtime/pm/features/Link/components/BubbleMenuLinkActions.d.vue.ts +2 -1
  64. package/dist/runtime/pm/features/Link/components/BubbleMenuLinkActions.vue.d.ts +2 -1
  65. package/dist/runtime/pm/features/Menus/components/MarkMenuManager.d.vue.ts +2 -1
  66. package/dist/runtime/pm/features/Menus/components/MarkMenuManager.vue.d.ts +2 -1
  67. package/dist/runtime/pm/generator.d.ts +30 -44
  68. package/dist/runtime/pm/generator.js +112 -109
  69. package/dist/runtime/pm/utils/generateRandomDoc.d.ts +3 -21
  70. package/dist/runtime/pm/utils/generateRandomDoc.js +36 -77
  71. package/dist/runtime/pm/utils/generator/createGeneratorConfig.d.ts +33 -0
  72. package/dist/runtime/pm/utils/generator/createGeneratorConfig.js +3 -0
  73. package/dist/runtime/pm/utils/generator/createPsuedoSentence.d.ts +4 -0
  74. package/dist/runtime/pm/utils/generator/createPsuedoSentence.js +16 -0
  75. package/dist/runtime/pm/utils/generator/createRandomChildCountGenerator.d.ts +14 -0
  76. package/dist/runtime/pm/utils/generator/createRandomChildCountGenerator.js +16 -0
  77. package/dist/runtime/pm/utils/generator/getTextOrMarkLength.d.ts +2 -0
  78. package/dist/runtime/pm/utils/generator/getTextOrMarkLength.js +7 -0
  79. package/dist/runtime/pm/utils/generator/influenceWithDepth.d.ts +6 -0
  80. package/dist/runtime/pm/utils/generator/influenceWithDepth.js +6 -0
  81. package/dist/runtime/pm/utils/generator/sometimesZero.d.ts +13 -0
  82. package/dist/runtime/pm/utils/generator/sometimesZero.js +7 -0
  83. package/package.json +59 -58
  84. package/src/runtime/components/CodeBlockThemePicker.vue +0 -1
  85. package/src/runtime/pm/features/DocumentApi/DocumentApi.ts +3 -0
  86. package/src/runtime/pm/features/DocumentApi/composables/useTestDocumentApi.ts +2 -1
  87. package/src/runtime/pm/features/EmbeddedDocument/components/EmbeddedDocumentPicker.vue +3 -4
  88. package/src/runtime/pm/features/Iframe/components/IframeNodeView.vue +0 -1
  89. package/src/runtime/pm/generator.ts +104 -151
  90. package/src/runtime/pm/schema.ts +2 -2
  91. package/src/runtime/pm/utils/generateRandomDoc.ts +47 -124
  92. package/src/runtime/pm/utils/generator/createGeneratorConfig.ts +56 -0
  93. package/src/runtime/pm/utils/generator/createPsuedoSentence.ts +18 -0
  94. package/src/runtime/pm/utils/generator/createRandomChildCountGenerator.ts +31 -0
  95. package/src/runtime/pm/utils/generator/getTextOrMarkLength.ts +9 -0
  96. package/src/runtime/pm/utils/generator/influenceWithDepth.ts +11 -0
  97. package/src/runtime/pm/utils/generator/sometimesZero.ts +16 -0
  98. package/dist/runtime/pm/utils/generateRandomTree.d.ts +0 -50
  99. package/dist/runtime/pm/utils/generateRandomTree.js +0 -38
  100. package/src/runtime/pm/utils/generateRandomTree.ts +0 -100
@@ -3,70 +3,63 @@ import { nanoid } from "nanoid";
3
3
  import { builders } from "prosemirror-test-builder";
4
4
  import { schema } from "./schema.js";
5
5
  export const pm = builders(schema);
6
- export function getTextOrMarkLength(textOrMark) {
7
- if (typeof textOrMark === "string") {
8
- return textOrMark.length;
9
- }
10
- const textNodes = textOrMark.flat.map((_) => "text" in _ ? _.text.length : getTextOrMarkLength(_));
11
- return textNodes.reduce((a, b) => a + b, 0);
12
- }
13
- export function createGeneratorConfig(config) {
14
- return config;
15
- }
16
- function createPsuedoSentence() {
17
- const sentenceLength = faker.number.int({ min: 0, max: 1e3 });
18
- const sentence = Array.from(
19
- { length: sentenceLength },
20
- () => faker.string.sample({ min: 0, max: 1e3 })
21
- ).join(" ");
22
- return sentence;
23
- }
6
+ import { highlightJsLanaguages } from "./features/CodeBlock/highlightJsInfo.js";
7
+ import { createGeneratorConfig } from "./utils/generator/createGeneratorConfig.js";
8
+ import { createPsuedoSentence } from "./utils/generator/createPsuedoSentence.js";
9
+ import { createRandomChildCountGenerator } from "./utils/generator/createRandomChildCountGenerator.js";
10
+ import { sometimesZero } from "./utils/generator/sometimesZero.js";
24
11
  export const generatorConfig = [
25
12
  createGeneratorConfig({
26
- isRoot: true,
27
13
  parents: { type: "node", names: ["doc"] },
28
- ignoreValidation: true,
29
- children: { type: "node", names: ["item"] },
30
- create: (_parent, children) => {
14
+ children: {
15
+ type: "node",
16
+ names: [["item", 1]]
17
+ },
18
+ count: createRandomChildCountGenerator({ max: 50, zeroChance: 0 }),
19
+ create: (pm2, _parent, children) => {
31
20
  if (!children || children.length === 0) {
32
- return pm.doc(pm.list(pm.item({ blockId: nanoid(10) }, pm.paragraph({}, ""))));
21
+ return pm2.doc(pm2.list(pm2.item({ blockId: nanoid(10) }, pm2.paragraph({}, ""))));
33
22
  }
34
- return pm.doc(pm.list(...children));
23
+ return pm2.doc(pm2.list(...children));
35
24
  }
36
25
  }),
37
26
  createGeneratorConfig({
38
- parents: { type: "node", names: ["list"] },
39
- children: { type: "node", names: ["item"] },
40
- create: (_parent, children) => {
27
+ parents: { type: "node", names: ["list", "doc"] },
28
+ children: { type: "node", names: [
29
+ ["item", 1]
30
+ ] },
31
+ count: createRandomChildCountGenerator({ max: 30 }),
32
+ create: (pm2, _parent, children) => {
41
33
  if (!children || children.length === 0) {
42
- return pm.list(pm.item({ blockId: nanoid(10) }, pm.paragraph("")));
34
+ return pm2.list(pm2.item({ blockId: nanoid(10) }, pm2.paragraph("")));
43
35
  }
44
- return pm.list(...children);
36
+ return pm2.list(...children);
45
37
  }
46
38
  }),
47
39
  createGeneratorConfig({
48
40
  parents: { type: "node", names: ["item"] },
49
41
  children: { type: "node", names: [
50
- "paragraph",
51
- "heading",
52
- "codeBlock",
53
- "embeddedDoc",
54
- "iframe",
55
- "table",
56
- "image",
57
- "blockquote"
42
+ ["paragraph", 14],
43
+ ["heading", 4],
44
+ ["codeBlock", 2],
45
+ ["table", 2],
46
+ ["image", 3],
47
+ ["blockquote", 4],
48
+ ["embeddedDoc", 1],
49
+ ["iframe", 1]
58
50
  ] },
59
- create: (_parent, children) => {
51
+ count: createRandomChildCountGenerator({ max: 20, depthInfluence: 0.5 }),
52
+ create: (pm2, _parent, children) => {
60
53
  if (!children || children.length === 0) {
61
- return pm.item({ blockId: nanoid(10) }, pm.paragraph(""));
54
+ return pm2.item({ blockId: nanoid(10) }, pm2.paragraph(""));
62
55
  }
63
- return pm.item(
56
+ return pm2.item(
64
57
  { blockId: nanoid(10) },
65
58
  children[0],
66
- ...children.length > 1 ? [pm.list(
59
+ ...children.length > 1 ? [pm2.list(
67
60
  {},
68
61
  ...children.slice(1).map(
69
- (_) => pm.item({ blockId: nanoid(10) }, _)
62
+ (_) => pm2.item({ blockId: nanoid(10) }, _)
70
63
  )
71
64
  )] : []
72
65
  );
@@ -74,8 +67,12 @@ export const generatorConfig = [
74
67
  }),
75
68
  createGeneratorConfig({
76
69
  parents: { type: "node", names: ["blockquote"] },
77
- children: { type: "node", names: ["paragraph", "cite"] },
78
- create: (_parent, children) => {
70
+ children: { type: "node", names: [
71
+ ["paragraph", 1],
72
+ ["cite", 1]
73
+ ] },
74
+ count: () => faker.number.int({ min: 1, max: 2 }),
75
+ create: (pm2, _parent, children) => {
79
76
  const parTypes = [];
80
77
  const citeTypes = [];
81
78
  for (const child of children) {
@@ -86,77 +83,88 @@ export const generatorConfig = [
86
83
  }
87
84
  }
88
85
  if (parTypes.length === 0) {
89
- return pm.blockquote({}, pm.paragraph(""));
86
+ return pm2.blockquote({}, pm2.paragraph(""));
90
87
  } else {
91
- const someCite = citeTypes.find((_) => _.textContent.length > 0) ?? pm.cite({}, createPsuedoSentence());
92
- return pm.blockquote({}, ...parTypes, someCite);
88
+ const someCite = citeTypes.find((_) => _.textContent.length > 0) ?? pm2.cite({}, createPsuedoSentence({ min: 1 }));
89
+ return pm2.blockquote({}, ...parTypes, someCite);
93
90
  }
94
91
  }
95
92
  }),
96
93
  createGeneratorConfig({
97
94
  parents: { type: "node", names: ["table"] },
98
95
  children: { type: "node", names: [
99
- "tableHeader",
100
- "tableCell"
96
+ ["tableCell", 1]
101
97
  ] },
102
- ignoreValidation: true,
103
- // we're handling the in-between tableRow nodes
104
- create: (_parent, children) => {
105
- const headerType = [];
106
- const cellType = [];
107
- for (const child of children) {
108
- if (child.type.name === "tableHeader") {
109
- headerType.push(child);
110
- } else if (child.type.name === "tableCell") {
111
- cellType.push(child);
112
- }
113
- }
114
- const colNum = headerType.length;
115
- if (colNum === 0) {
116
- return pm.table({}, pm.tableRow({}, pm.tableCell(pm.paragraph(""))));
117
- }
118
- const rowCount = Math.ceil(cellType.length / colNum);
98
+ count: createRandomChildCountGenerator({ max: 50, depthInfluence: 0.5, zeroChance: 0 }),
99
+ create: (pm2, _parent, children) => {
100
+ const colNum = faker.number.int({ min: 1, max: Math.min(children.length, 10) });
101
+ const rowCount = Math.floor(children.length / colNum);
119
102
  const rows = [];
103
+ const addHeader = faker.datatype.boolean();
104
+ if (addHeader) {
105
+ const nodes = Array.from(
106
+ { length: colNum },
107
+ () => pm2.tableHeader({}, pm2.paragraph({}, createPsuedoSentence({ min: 1 })))
108
+ );
109
+ rows.push(pm2.tableRow({}, ...nodes));
110
+ }
120
111
  for (let i = 0; i < rowCount; i++) {
121
- rows.push(pm.tableRow({}, ...cellType.slice(i * colNum, (i + 1) * colNum)));
112
+ const rowChildren = children.slice(i * colNum, (i + 1) * colNum);
113
+ if (rowChildren.length === 0) continue;
114
+ rows.push(pm2.tableRow({}, ...rowChildren));
122
115
  }
123
- return pm.table({}, pm.tableRow({}, ...headerType), ...rows);
116
+ if (rows.length === 0) return void 0;
117
+ return pm2.table({}, ...rows);
124
118
  }
125
119
  }),
126
120
  createGeneratorConfig({
127
- parents: { type: "node", names: ["tableHeader", "tableCell"] },
128
- children: { type: "node", names: ["paragraph"] },
129
- create: (_parent, children) => {
121
+ parents: { type: "node", names: ["tableCell"] },
122
+ children: { type: "node", names: [
123
+ ["paragraph", 1]
124
+ ] },
125
+ count: () => sometimesZero(1),
126
+ create: (pm2, _parent, children) => {
130
127
  if (!children || children.length === 0) {
131
- return pm.tableCell(pm.paragraph(""));
128
+ return pm2.tableCell(pm2.paragraph(""));
132
129
  }
133
- return pm.tableCell(...children.slice(0, 1));
130
+ return pm2.tableCell(...children.slice(0, 1));
134
131
  }
135
132
  }),
136
133
  createGeneratorConfig({
137
- parents: { type: "node", names: ["paragraph", "heading", "codeBlock", "cite", "heading"] },
134
+ parents: { type: "text", names: ["paragraph", "heading", "codeBlock", "cite", "heading"] },
138
135
  children: { type: "text", names: [
139
- "bold",
140
- "italic",
141
- "underline",
142
- "strike",
143
- "subscript",
144
- "superscript",
145
- "code",
146
- "link"
136
+ ["bold", 1],
137
+ ["italic", 1],
138
+ ["underline", 1],
139
+ ["strike", 1],
140
+ ["subscript", 1],
141
+ ["superscript", 1],
142
+ ["code", 1],
143
+ ["link", 1],
144
+ ["text", 10]
147
145
  ] },
148
- create(parent, children) {
146
+ count: createRandomChildCountGenerator({ max: 5, depthInfluence: 0.2 }),
147
+ create(pm2, parent, children) {
149
148
  if (parent === "heading") {
150
- return pm[parent]({ level: 1 }, ...children);
149
+ return pm2[parent]({ level: faker.number.int({ min: 1, max: 6 }) }, ...children);
151
150
  }
152
151
  if (parent === "cite") {
153
- return pm[parent]({}, faker.lorem.sentence());
152
+ return pm2[parent]({}, faker.lorem.sentence());
154
153
  }
155
154
  if (parent === "paragraph") {
156
155
  const someIndex = faker.number.int({ min: 0, max: children.length });
157
- children.splice(someIndex, 0, pm.hardBreak());
156
+ children.splice(someIndex, 0, pm2.hardBreak());
157
+ }
158
+ if (parent === "codeBlock") {
159
+ const lang = faker.helpers.arrayElement([...Object.keys(highlightJsLanaguages.aliases), void 0]);
160
+ const lines = faker.number.int({ min: 1, max: 10 });
161
+ const code = Array.from(
162
+ { length: lines },
163
+ () => createPsuedoSentence()
164
+ ).join("\n");
165
+ return pm2[parent]({ language: lang }, code);
158
166
  }
159
- return pm[parent]({}, ...children);
167
+ return pm2[parent]({}, ...children);
160
168
  }
161
169
  }),
162
170
  createGeneratorConfig({
@@ -172,33 +180,28 @@ export const generatorConfig = [
172
180
  "link"
173
181
  ]
174
182
  },
175
- // note the addition of text
183
+ skipChild(parentType, childType, depth) {
184
+ if (depth > 20) return true;
185
+ return parentType === childType;
186
+ },
176
187
  children: { type: "text", names: [
177
- "bold",
178
- "italic",
179
- "underline",
180
- "strike",
181
- "subscript",
182
- "superscript",
183
- "link",
184
- "text"
188
+ ["bold", 1],
189
+ ["italic", 1],
190
+ ["underline", 1],
191
+ ["strike", 1],
192
+ ["subscript", 1],
193
+ ["superscript", 1],
194
+ ["link", 1],
195
+ ["text", 10]
185
196
  ] },
186
- ignoreValidation: ["text"]
187
- // text is not a real node
188
- // create: (parent, children) => {
189
- // return pm[parent]({}, ...children as any) as any
190
- // }
191
- }),
192
- createGeneratorConfig({
193
- ignoreValidation: true,
194
- // text is not a real node
195
- parents: { type: "text", names: ["code"] },
196
- children: { type: "text", names: ["text"] }
197
+ count: createRandomChildCountGenerator({ max: 5, depthInfluence: 0.7 }),
198
+ create: (pm2, parent, children) => {
199
+ return pm2[parent]({}, ...children);
200
+ }
197
201
  }),
198
202
  createGeneratorConfig({
199
203
  parents: { type: "text", names: ["text"] },
200
- children: { type: "text" },
201
- create: (_parent) => {
204
+ create: () => {
202
205
  return createPsuedoSentence();
203
206
  }
204
207
  })
@@ -1,23 +1,5 @@
1
1
  import type { Node } from "@tiptap/pm/model";
2
2
  import type { builders } from "prosemirror-test-builder";
3
- import { generateRandomTree } from "./generateRandomTree.js";
4
- import type { GeneratorConfigEntry } from "../generator.js";
5
- /**
6
- * Generates a random doc using faker js.
7
- *
8
- * A config describing how to create the doc is required. One is available under `pm/generator`.
9
- *
10
- * @experimental
11
- */
12
- export declare function generateRandomDoc<TBuilder extends ReturnType<typeof builders>, TData extends string>(builder: TBuilder, config?: GeneratorConfigEntry<any, any, any, any>[], treeOptions?: Parameters<typeof generateRandomTree>[1], { checkAfterNodeCreation }?: {
13
- /**
14
- * Checks if every returned node is valid for debugging invalid generator configs.
15
- *
16
- * A check is always done on the root node at the end regardless of this option.
17
- *
18
- * It is very slow (each check, rechecks children).
19
- *
20
- * @default false
21
- */
22
- checkAfterNodeCreation?: boolean;
23
- }): Node;
3
+ import type { GeneratorConfigEntry } from "./generator/createGeneratorConfig.js";
4
+ export declare function generateRandomDoc<TBuilder extends ReturnType<typeof builders>>(pm: TBuilder, config?: GeneratorConfigEntry<any, any, any, any>[]): Node;
5
+ export declare function pickWeightedChild(options: [string, number][]): string | undefined;
@@ -1,83 +1,42 @@
1
- import { unreachable } from "@alanscodelog/utils/unreachable";
2
1
  import { faker } from "@faker-js/faker";
3
- import { generateRandomTree } from "./generateRandomTree.js";
4
2
  import { generatorConfig } from "../generator.js";
5
- export function generateRandomDoc(builder, config = generatorConfig, treeOptions, {
6
- checkAfterNodeCreation = false
7
- } = {}) {
8
- const schema = builder.schema;
9
- const map = {};
10
- function schemaFilter(name) {
11
- return name !== "text" && name !== schema.topNodeType.name;
12
- }
13
- for (const entry of config) {
14
- for (const node of entry.parents.names) {
15
- if (node === "") throw new Error(`Empty node name ""`);
16
- const type = entry.parents.type === "node" ? schema.nodes[node] : schema.marks[node];
17
- if (type === void 0 && node !== "text") {
18
- throw new Error(`${node} (on entry of type ${entry.parents.type}) not found in schema. Valid names are ${Object.keys(entry.parents.type === "node" ? schema.nodes : schema.marks)}`);
19
- }
20
- for (const node2 of entry.parents.names) {
21
- const subEntry = { ...entry };
22
- if (entry.ignoreValidation === void 0 || entry.ignoreValidation !== true) {
23
- const childrenToValidate = (entry.children.names ?? []).filter((_) => !Array.isArray(entry.ignoreValidation) || !entry.ignoreValidation.includes(_));
24
- for (const child of childrenToValidate) {
25
- if (child === "") throw new Error(`Empty node name ""`);
26
- if (entry.children.type === "node") {
27
- const childType = schema.nodes[child];
28
- const possibleNames = [childType.name, ...(childType.spec.group ?? "").split(" ").filter((e) => e !== "")];
29
- const isAllowedChild = type.spec.content && possibleNames.some((n) => type.spec.content.includes(n));
30
- if (!(isAllowedChild && schemaFilter(childType.name))) {
31
- throw new Error(`Node ${node2} cannot contain child of type ${child}`);
32
- }
33
- } else {
34
- const childType = schema.marks[child];
35
- if (entry.parents.type === "node") {
36
- } else {
37
- const possibleNames = [childType.name, ...(childType.spec.group ?? "").split(" ").filter((e) => e !== "")];
38
- const t = type;
39
- const isExcluded = t.spec.excludes !== void 0 && possibleNames.some((n) => {
40
- return t.spec.excludes.includes(n) || t.spec.excludes.includes("_");
41
- });
42
- if (isExcluded) {
43
- throw new Error(`Mark ${node2} cannot contain mark child of type ${child}`);
44
- }
45
- }
46
- }
47
- }
48
- }
49
- map[node2] = { ...subEntry };
50
- }
3
+ export function generateRandomDoc(pm, config = generatorConfig) {
4
+ function generateNode(pm2, nodeConfig, parentType, depth = 0) {
5
+ if (!nodeConfig.parents.names.includes(parentType)) {
6
+ throw new Error(`Node config ${nodeConfig.parents.names.join(", ")} does not include parent type ${parentType}`);
51
7
  }
52
- }
53
- const children = generateRandomTree({
54
- parentData: (data) => {
55
- if (data === "") return "";
56
- if (!data) unreachable();
57
- if (!map[data] || !map[data].children.names || map[data].children.names.length === 0) {
58
- return "";
59
- } else {
60
- const picked = faker.helpers.arrayElement(map[data].children.names);
61
- return picked;
8
+ const nodes = [];
9
+ if ("count" in nodeConfig) {
10
+ const childrenCount = nodeConfig.count(depth);
11
+ for (let i = 0; i < childrenCount; i++) {
12
+ const type = pickWeightedChild(nodeConfig.children.names);
13
+ if (type === void 0) continue;
14
+ if (nodeConfig?.skipChild?.(parentType, type, depth)) continue;
15
+ const childConfig = config.find((c) => {
16
+ return c.parents.names.includes(type);
17
+ });
18
+ if (!childConfig) continue;
19
+ const child = generateNode(pm2, childConfig, type, depth + 1);
20
+ if (child === void 0) continue;
21
+ nodes.push(child);
62
22
  }
63
- },
64
- createNode: (children2, _isLeaf, data) => {
65
- if (data === "") return void 0;
66
- if (!data) unreachable();
67
- const type = schema.nodes[data];
68
- if (!type) return void 0;
69
- const parent = map[data]?.create?.(data, children2) ?? builder[data]({}, ...children2);
70
- if (checkAfterNodeCreation) {
71
- ;
72
- parent?.check?.();
73
- }
74
- return parent;
75
23
  }
76
- }, {
77
- ...treeOptions,
78
- initialData: schema.topNodeType.name
79
- });
80
- const doc = map[schema.topNodeType.name]?.create?.(schema.topNodeType.name, children) ?? builder[schema.topNodeType.name]({}, ...children);
81
- doc.check?.();
82
- return doc;
24
+ return nodeConfig.create(pm2, parentType, nodes);
25
+ }
26
+ const firstEntry = config[0];
27
+ if (firstEntry === void 0) throw new Error("No config entries found.");
28
+ if (!("children" in firstEntry)) throw new Error("No children found in the first config entry. The first entry must have children.");
29
+ const startingType = pickWeightedChild(firstEntry.parents.names.map((name) => [name, 1]));
30
+ if (startingType === void 0) throw new Error("No starting type found.");
31
+ return generateNode(pm, config[0], startingType);
32
+ }
33
+ export function pickWeightedChild(options) {
34
+ const totalWeight = options.reduce((sum, [, weight]) => sum + weight, 0);
35
+ if (totalWeight === 0) return void 0;
36
+ let random = faker.number.float({ min: 0, max: totalWeight });
37
+ for (const [name, weight] of options) {
38
+ if (random < weight) return name;
39
+ random -= weight;
40
+ }
41
+ return options[0][0];
83
42
  }
@@ -0,0 +1,33 @@
1
+ import type { Mark, Node } from "@tiptap/pm/model";
2
+ import type { builders } from "prosemirror-test-builder";
3
+ export declare function createGeneratorConfig<TBuilder extends ReturnType<typeof builders>, TChildType extends "node" | "text", TParentType extends "node" | "text", TChildNodeType extends TChildType extends "node" ? Node : (Mark | string), TParentNodeType extends TParentType extends "node" ? Node : (Mark | string)>(config: GeneratorConfigEntry<TBuilder, TChildType, TParentType, TChildNodeType, TParentNodeType>): GeneratorConfigEntry<TBuilder, TChildType, TParentType, TChildNodeType, TParentNodeType>;
4
+ export type GeneratorConfigEntry<TBuilder extends ReturnType<typeof builders>, TChildType extends "node" | "text" = "node" | "text", TParentType extends "node" | "text" = "node" | "text", TChildNodeType extends TChildType extends "node" ? Node : (Mark | string) = TChildType extends "node" ? Node : (Mark | string), TParentNodeType extends TParentType extends "node" ? Node : (Mark | string) = TParentType extends "node" ? Node : (Mark | string)> = {
5
+ /**
6
+ * Creates the node. Can return undefined to "terminate" the branch being created, the node will be filtered out of the nodes passed to it's parent.
7
+ *
8
+ * Note also, it is not required to use the children. You can ignore them or use a subset or create different ones if needed (some node types *require* text and if count returns 0 children will be empty, which can cause issues).
9
+ */
10
+ create: (pm: TBuilder, parentType: string, children: TChildNodeType[]) => TParentNodeType | undefined;
11
+ parents: {
12
+ /**
13
+ * The type of the parent (node or "text") where "text" is a string or mark.
14
+ * Determines the call signature of the create function.
15
+ */
16
+ type: TParentType;
17
+ /** Possible parent nodes that can have the given children. The children need not be direct descendants or real prosemirror nodes since `create` is what determines how they're created. */
18
+ names: string[];
19
+ };
20
+ } & ({} | {
21
+ skipChild?: (parentType: string, childType: string, depth: number) => boolean;
22
+ children: {
23
+ /** The type of children (node or "text") where "text" is a string or mark. */
24
+ type: TChildType;
25
+ names: [name: string, weight: number][];
26
+ };
27
+ /**
28
+ * How many children to generate, is passed the depth so you can make the tree "slimmer" as it does deeper.
29
+ *
30
+ * See the `sometimesZero`, `influenceWithDepth` and `createRandomChildCountGenerator` helpers to make this easier to control.
31
+ */
32
+ count: (depth: number) => number;
33
+ });
@@ -0,0 +1,3 @@
1
+ export function createGeneratorConfig(config) {
2
+ return config;
3
+ }
@@ -0,0 +1,4 @@
1
+ export declare function createPsuedoSentence({ min, max }?: {
2
+ min?: number;
3
+ max?: number;
4
+ }): string;
@@ -0,0 +1,16 @@
1
+ import { faker } from "@faker-js/faker";
2
+ export function createPsuedoSentence({ min = 0, max = 10 } = {}) {
3
+ const sentenceLength = faker.number.int({ min, max });
4
+ const sentence = Array.from(
5
+ { length: sentenceLength },
6
+ () => {
7
+ const doLorem = faker.number.float() < 0.5;
8
+ if (doLorem) {
9
+ return faker.lorem.word();
10
+ } else {
11
+ return faker.string.sample({ min: 1, max: 10 });
12
+ }
13
+ }
14
+ ).join(" ");
15
+ return sentence;
16
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Combines `sometimesZero` and `influenceWithDepth` to create a function that returns a random number to pass to `count`.
3
+ *
4
+ * ```ts
5
+ * count: createRandomChildCountGenerator({ max: 10, depthInfluence: 0.5 })
6
+ * // or
7
+ * count: (depth: number) => createRandomChildCountGenerator({ max: 10, depthInfluence: 0.5 })(depth)
8
+ * ```
9
+ */
10
+ export declare function createRandomChildCountGenerator({ max, zeroChance, depthInfluence }: {
11
+ max: number;
12
+ zeroChance?: number;
13
+ depthInfluence?: number;
14
+ }): (depth: number) => number;
@@ -0,0 +1,16 @@
1
+ import { faker } from "@faker-js/faker";
2
+ import { influenceWithDepth } from "./influenceWithDepth.js";
3
+ import { sometimesZero } from "./sometimesZero.js";
4
+ export function createRandomChildCountGenerator({
5
+ max = 10,
6
+ zeroChance = 0.05,
7
+ depthInfluence = 1
8
+ }) {
9
+ return (depth) => {
10
+ depth++;
11
+ return sometimesZero(
12
+ faker.number.int({ min: 1, max: influenceWithDepth(max, depth, depthInfluence) || 1 }),
13
+ zeroChance
14
+ );
15
+ };
16
+ }
@@ -0,0 +1,2 @@
1
+ import type { Mark } from "@tiptap/pm/model";
2
+ export declare function getTextOrMarkLength(textOrMark: string | Mark): number;
@@ -0,0 +1,7 @@
1
+ export function getTextOrMarkLength(textOrMark) {
2
+ if (typeof textOrMark === "string") {
3
+ return textOrMark.length;
4
+ }
5
+ const textNodes = textOrMark.flat.map((_) => "text" in _ ? _.text.length : getTextOrMarkLength(_));
6
+ return textNodes.reduce((a, b) => a + b, 0);
7
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Decreases the given number by dividing it by the depth + 1 (so it it's not zero). Returns a floored *int*.
3
+ *
4
+ * Intended to help decrease the number of nodes as the depth increases. Can be adjusted by the strength parameter, to, for example, decrease the adjustment for nodes that tend to be deeper.
5
+ */
6
+ export declare function influenceWithDepth(num: number, depth: number, strength?: number): number;
@@ -0,0 +1,6 @@
1
+ export function influenceWithDepth(num, depth, strength = 1) {
2
+ if (num === 0) return 0;
3
+ depth++;
4
+ const adjusted = Math.floor(num / (depth * strength));
5
+ return adjusted;
6
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Returns 0 instead of the given number depending on the chance passed (0.05 by default).
3
+ *
4
+ * This is useful to avoid too many empty nodes without having to increase the max a lot.
5
+ *
6
+ * You can pass chance 0 to always return the given number.
7
+ */
8
+ export declare function sometimesZero(num: number, chance?: number, getRandom?: (options?: number | {
9
+ min?: number;
10
+ max?: number;
11
+ fractionDigits?: number;
12
+ multipleOf?: number;
13
+ }) => number): number;
@@ -0,0 +1,7 @@
1
+ import { faker } from "@faker-js/faker";
2
+ export function sometimesZero(num, chance = 0.05, getRandom = faker.number.float) {
3
+ if (getRandom() < chance) {
4
+ return 0;
5
+ }
6
+ return num;
7
+ }