ckeditor5-elements-schema 0.0.0-stage → 49.0.0-alpha.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -2
  3. package/dist/augmentation.d.ts +19 -0
  4. package/dist/augmentation.d.ts.map +1 -0
  5. package/dist/augmentation.js +2 -0
  6. package/dist/augmentation.js.map +1 -0
  7. package/dist/canHold.d.ts +10 -0
  8. package/dist/canHold.d.ts.map +1 -0
  9. package/dist/canHold.js +22 -0
  10. package/dist/canHold.js.map +1 -0
  11. package/dist/compileElementSchema.d.ts +41 -0
  12. package/dist/compileElementSchema.d.ts.map +1 -0
  13. package/dist/compileElementSchema.js +60 -0
  14. package/dist/compileElementSchema.js.map +1 -0
  15. package/dist/convertElementSchema.d.ts +16 -0
  16. package/dist/convertElementSchema.d.ts.map +1 -0
  17. package/dist/convertElementSchema.js +43 -0
  18. package/dist/convertElementSchema.js.map +1 -0
  19. package/dist/elementSchema.d.ts +47 -0
  20. package/dist/elementSchema.d.ts.map +1 -0
  21. package/dist/elementSchema.js +108 -0
  22. package/dist/elementSchema.js.map +1 -0
  23. package/dist/elementSchemaError.d.ts +12 -0
  24. package/dist/elementSchemaError.d.ts.map +1 -0
  25. package/dist/elementSchemaError.js +16 -0
  26. package/dist/elementSchemaError.js.map +1 -0
  27. package/dist/index.d.ts +10 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +12 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/readDefinition.d.ts +18 -0
  32. package/dist/readDefinition.d.ts.map +1 -0
  33. package/dist/readDefinition.js +60 -0
  34. package/dist/readDefinition.js.map +1 -0
  35. package/dist/suggest.d.ts +5 -0
  36. package/dist/suggest.d.ts.map +1 -0
  37. package/dist/suggest.js +27 -0
  38. package/dist/suggest.js.map +1 -0
  39. package/dist/types.d.ts +105 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/validate.d.ts +12 -0
  44. package/dist/validate.d.ts.map +1 -0
  45. package/dist/validate.js +11 -0
  46. package/dist/validate.js.map +1 -0
  47. package/dist/validateElementSchema.d.ts +26 -0
  48. package/dist/validateElementSchema.d.ts.map +1 -0
  49. package/dist/validateElementSchema.js +145 -0
  50. package/dist/validateElementSchema.js.map +1 -0
  51. package/package.json +49 -3
  52. package/src/augmentation.ts +21 -0
  53. package/src/canHold.ts +29 -0
  54. package/src/compileElementSchema.ts +88 -0
  55. package/src/convertElementSchema.ts +51 -0
  56. package/src/elementSchema.ts +133 -0
  57. package/src/elementSchemaError.ts +21 -0
  58. package/src/index.ts +14 -0
  59. package/src/readDefinition.ts +98 -0
  60. package/src/suggest.ts +38 -0
  61. package/src/types.ts +123 -0
  62. package/src/validate.ts +20 -0
  63. package/src/validateElementSchema.ts +238 -0
@@ -0,0 +1,60 @@
1
+ import { suggest } from './suggest.js';
2
+ const TYPES = ['empty', 'block', 'container'];
3
+ export const ELEMENT_KEYS = ['type', 'isInline', 'allowChildren', 'allowIn', 'allowAttributes', 'editingTag'];
4
+ export const ELEMENT_NAME = /^[a-z][A-Za-z0-9]*$/;
5
+ export const ATTRIBUTE_NAME = /^[A-Za-z][\w.:-]*$/;
6
+ export const TAG_NAME = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
7
+ export function readKind(definition, path, report) {
8
+ const { type, isInline } = definition;
9
+ if (isInline !== undefined && typeof isInline !== 'boolean') {
10
+ report(`${path}.isInline`, 'must be true or false.');
11
+ }
12
+ if (type === undefined) {
13
+ report(`${path}.type`, 'is required: "empty", "block" or "container".');
14
+ return null;
15
+ }
16
+ if (typeof type !== 'string' || !TYPES.includes(type)) {
17
+ report(`${path}.type`, `must be "empty", "block" or "container", not ${JSON.stringify(type)}${typeof type === 'string' ? suggest(type, TYPES) : ''}.`);
18
+ return null;
19
+ }
20
+ return { type: type, isInline: isInline === true };
21
+ }
22
+ /**
23
+ * Reads a list of names, and reports what is not a list of unique, non-empty strings. Returns the list, or
24
+ * `undefined` when it is missing.
25
+ */
26
+ export function readNames(definition, key, path, report) {
27
+ const value = definition[key];
28
+ if (value === undefined) {
29
+ return undefined;
30
+ }
31
+ if (!Array.isArray(value)) {
32
+ report(`${path}.${key}`, `must be an array of names, e.g. ["a", "b"].`);
33
+ return undefined;
34
+ }
35
+ const names = [];
36
+ value.forEach((item, index) => {
37
+ const itemPath = `${path}.${key}[${String(index)}]`;
38
+ if (typeof item !== 'string' || !item) {
39
+ report(itemPath, 'must be a non-empty string.');
40
+ }
41
+ else if (names.includes(item)) {
42
+ report(itemPath, `"${item}" is listed twice.`);
43
+ }
44
+ else {
45
+ names.push(item);
46
+ }
47
+ });
48
+ return names;
49
+ }
50
+ export function checkKeys(object, allowed, path, report) {
51
+ for (const key of Object.keys(object)) {
52
+ if (!allowed.includes(key)) {
53
+ report(path ? `${path}.${key}` : key, `is not a known key${suggest(key, allowed)}. The keys are: ${allowed.map(name => `"${name}"`).join(', ')}.`);
54
+ }
55
+ }
56
+ }
57
+ export function isRecord(value) {
58
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
59
+ }
60
+ //# sourceMappingURL=readDefinition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readDefinition.js","sourceRoot":"","sources":["../src/readDefinition.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Returns ` Did you mean "x"?` for the closest candidate, when it is close enough to be a typo.
3
+ */
4
+ export declare function suggest(name: string, candidates: readonly string[]): string;
5
+ //# sourceMappingURL=suggest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAc3E"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Returns ` Did you mean "x"?` for the closest candidate, when it is close enough to be a typo.
3
+ */
4
+ export function suggest(name, candidates) {
5
+ let best;
6
+ let bestDistance = 3;
7
+ for (const candidate of candidates) {
8
+ const distance = editDistance(name.toLowerCase(), candidate.toLowerCase());
9
+ if (distance < bestDistance) {
10
+ best = candidate;
11
+ bestDistance = distance;
12
+ }
13
+ }
14
+ return best ? ` (did you mean "${best}"?)` : '';
15
+ }
16
+ function editDistance(a, b) {
17
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
18
+ for (let i = 1; i <= a.length; i++) {
19
+ const current = [i];
20
+ for (let j = 1; j <= b.length; j++) {
21
+ current[j] = Math.min((previous[j] ?? 0) + 1, (current[j - 1] ?? 0) + 1, (previous[j - 1] ?? 0) + (a[i - 1] === b[j - 1] ? 0 : 1));
22
+ }
23
+ previous = current;
24
+ }
25
+ return previous[b.length] ?? 0;
26
+ }
27
+ //# sourceMappingURL=suggest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suggest.js","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,UAA6B;IAClE,IAAI,IAAwB,CAAC;IAC7B,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC;QAE3E,IAAI,QAAQ,GAAG,YAAY,EAAE,CAAC;YAC7B,IAAI,GAAG,SAAS,CAAC;YACjB,YAAY,GAAG,QAAQ,CAAC;QACzB,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC,CAAC,CAAC,mBAAmB,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,CAAS,EAAE,CAAS;IACzC,IAAI,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAEzE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;QAEpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CACpB,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EACtB,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EACzB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACxD,CAAC;QACH,CAAC;QAED,QAAQ,GAAG,OAAO,CAAC;IACpB,CAAC;IAED,OAAO,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The kinds of elements of an element schema:
3
+ *
4
+ * * `empty`: has no content, like an image or a line break. It is an object: selected as a whole.
5
+ * * `block`: can contain text and inline elements, like a paragraph or a heading. With `isInline`, it is an inline
6
+ * element with text, like a bold with an `id`.
7
+ *
8
+ * * `container`: cannot contain text; holds blocks, empty block elements and other containers, like a block quote or a
9
+ * note.
10
+ *
11
+ * Every type can be inline (see {@link ElementSchemaElement#isInline}): the type says what the element holds, `isInline`
12
+ * where and how it is laid out. Inline elements are allowed only in blocks.
13
+ */
14
+ export type ElementSchemaType = 'empty' | 'block' | 'container';
15
+ /**
16
+ * The kind of an element: what it is, and whether it is inline: laid out in a line with its siblings, instead of on a
17
+ * line of its own. Any type can be inline.
18
+ */
19
+ export interface ElementSchemaKind {
20
+ type: ElementSchemaType;
21
+ isInline: boolean;
22
+ }
23
+ /**
24
+ * The definition of one element of an element schema.
25
+ */
26
+ export interface ElementSchemaElement {
27
+ /**
28
+ * What the element is: `'empty'`, `'block'` or `'container'`.
29
+ */
30
+ type: ElementSchemaType;
31
+ /**
32
+ * Whether the element is inline: it is about the position and the layout of the element, not about text. An inline
33
+ * element is laid out in a line with its siblings, where a block-level one has a line of its own. Any type can be:
34
+ *
35
+ * * `empty`: like an inline image, or a line break;
36
+ * * `block`: an element with text in a line of text, like a bold that is an element, with attributes;
37
+ * * `container`: a box that is not typable, in a line with other boxes, e.g. several in a block.
38
+ *
39
+ * Inline elements are allowed only in blocks: by default, where text is. A block holds text and any number of inline
40
+ * elements; containers and the root hold no inline elements. Default: `false`.
41
+ */
42
+ isInline?: boolean;
43
+ /**
44
+ * The elements allowed inside this one, in addition to what its type allows: names of elements of the schema, or of
45
+ * elements of other plugins, and `'$text'`. As in CKEditor's `allowChildren`.
46
+ *
47
+ * What a type allows by default: a `block` has text and inline elements; a `container` has blocks, containers and
48
+ * `empty` block-level elements.
49
+ */
50
+ allowChildren?: string[];
51
+ /**
52
+ * The elements this one is allowed in, in addition to what its type allows: names of elements, and `'$root'`. As in
53
+ * CKEditor's `allowIn`.
54
+ *
55
+ * Unlike in CKEditor, when it is given it **replaces** the default places (a block-level element is allowed in the
56
+ * root and in containers; an inline element, where text is), so that an element can be restricted to its parent,
57
+ * e.g. a row to a table.
58
+ */
59
+ allowIn?: string[];
60
+ /**
61
+ * The attributes the element can have, as in CKEditor's `allowAttributes`.
62
+ */
63
+ allowAttributes?: string[];
64
+ /**
65
+ * The tag of the element in the editing view, e.g. `'section'`, when the converters of the schema are on (the
66
+ * `elementSchemaConverters` option). The data has `div` or `span`, whatever it is. Default: the tag of the data.
67
+ */
68
+ editingTag?: string;
69
+ }
70
+ /**
71
+ * An element schema: the definitions of the elements, by name.
72
+ */
73
+ export type ElementSchemaDefinition = Record<string, ElementSchemaElement>;
74
+ /**
75
+ * What the `elementSchema` option accepts: an {@link ElementSchemaDefinition}, or a JSON file imported as it is, whose
76
+ * strings TypeScript does not know are the allowed ones, and whose arrays are not known to be mutable. Either way, it is
77
+ * validated when the editor is created.
78
+ */
79
+ export type ElementSchemaInput = Record<string, {
80
+ [Key in keyof ElementSchemaElement]: Key extends 'type' ? string : ElementSchemaElement[Key] extends string[] | undefined ? readonly string[] | undefined : ElementSchemaElement[Key];
81
+ }>;
82
+ /**
83
+ * A problem of an element schema.
84
+ */
85
+ export interface ElementSchemaIssue {
86
+ /**
87
+ * Where the problem is, e.g. `note.allowChildren[1]`.
88
+ */
89
+ path: string;
90
+ /**
91
+ * What is wrong, and what to do about it.
92
+ */
93
+ message: string;
94
+ }
95
+ /**
96
+ * The options of {@link validateElementSchema}.
97
+ */
98
+ export interface ValidateElementSchemaOptions {
99
+ /**
100
+ * Elements that exist outside the schema, e.g. `paragraph` of the `Paragraph` plugin, by name. The schema can refer to
101
+ * them, but cannot declare them. Without them, the schema can refer only to its own elements.
102
+ */
103
+ external?: Readonly<Record<string, ElementSchemaKind>>;
104
+ }
105
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,CAAC;AAEhE;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAC;IAExB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CACtC,MAAM,EACN;KACE,GAAG,IAAI,MAAM,oBAAoB,GAAG,GAAG,SAAS,MAAM,GACpD,MAAM,GACN,oBAAoB,CAAC,GAAG,CAAC,SAAS,MAAM,EAAE,GAAG,SAAS,GACrD,SAAS,MAAM,EAAE,GAAG,SAAS,GAC7B,oBAAoB,CAAC,GAAG,CAAC;CAC7B,CACD,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;CACvD"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The part of the element schema that needs no editor and no browser: the validation, the compiling to schema
3
+ * definitions, the error and the types. It is in its own module, so that it can be used in Node.js, e.g. to validate a
4
+ * schema file in a test or in CI: the main entry loads CKEditor, which needs a DOM.
5
+ *
6
+ * @module ckeditor5-elements-schema/validate
7
+ */
8
+ export { validateElementSchema } from './validateElementSchema.js';
9
+ export { compileElementSchema, type CompiledElementSchema } from './compileElementSchema.js';
10
+ export { default as ElementSchemaError } from './elementSchemaError.js';
11
+ export type { ElementSchemaDefinition, ElementSchemaElement, ElementSchemaInput, ElementSchemaIssue, ElementSchemaKind, ElementSchemaType, ValidateElementSchemaOptions } from './types.js';
12
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACxE,YAAY,EACX,uBAAuB,EACvB,oBAAoB,EACpB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,4BAA4B,EAC5B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The part of the element schema that needs no editor and no browser: the validation, the compiling to schema
3
+ * definitions, the error and the types. It is in its own module, so that it can be used in Node.js, e.g. to validate a
4
+ * schema file in a test or in CI: the main entry loads CKEditor, which needs a DOM.
5
+ *
6
+ * @module ckeditor5-elements-schema/validate
7
+ */
8
+ export { validateElementSchema } from './validateElementSchema.js';
9
+ export { compileElementSchema } from './compileElementSchema.js';
10
+ export { default as ElementSchemaError } from './elementSchemaError.js';
11
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAA8B,MAAM,2BAA2B,CAAC;AAC7F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import type { ElementSchemaIssue, ValidateElementSchemaOptions } from './types.js';
2
+ /**
3
+ * Checks an element schema, so that it is not contradictory, and returns the problems. An empty array means that the
4
+ * schema is valid.
5
+ *
6
+ * The schema is rejected when it:
7
+ *
8
+ * * is not of the shape {@link ElementSchemaDefinition}: an object of elements, by name; has keys that are not part of
9
+ * it (typos are problems), or has values of the wrong type;
10
+ * * declares an element with an unknown `type`, or an `empty` element with `allowChildren`;
11
+ * * has attribute names that are not valid, or that differ only in case (HTML attribute names are not case sensitive),
12
+ * or an `editingTag` that is not the name of a tag;
13
+ * * refers to elements that do not exist (a name that is neither declared in the schema, nor in `external`), or lists the
14
+ * same name twice;
15
+ * * allows what its type cannot hold: text or an inline element in a container or in the root, a block-level (not inline)
16
+ * element in a block, or anything in an `empty` element, whichever side says it: `allowChildren` of the parent, or
17
+ * `allowIn` of the child.
18
+ *
19
+ * `isInline` is about the position and the layout of an element, so it is valid for every type: an inline element is
20
+ * laid out in a line with its siblings. Inline elements are allowed only in blocks, which can hold several of them,
21
+ * e.g. inline containers, or inline images between text.
22
+ *
23
+ * It does not need an editor. The editor of `ElementSchema` runs it with the elements of the other plugins as `external`.
24
+ */
25
+ export declare function validateElementSchema(input: unknown, { external }?: ValidateElementSchemaOptions): ElementSchemaIssue[];
26
+ //# sourceMappingURL=validateElementSchema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateElementSchema.d.ts","sourceRoot":"","sources":["../src/validateElementSchema.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,kBAAkB,EAAqB,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAEtG;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CACpC,KAAK,EAAE,OAAO,EACd,EAAE,QAAa,EAAE,GAAE,4BAAiC,GAClD,kBAAkB,EAAE,CAkKtB"}
@@ -0,0 +1,145 @@
1
+ import { ROOT, whyNot } from './canHold.js';
2
+ import { ATTRIBUTE_NAME, ELEMENT_KEYS, ELEMENT_NAME, TAG_NAME, checkKeys, isRecord, readKind, readNames } from './readDefinition.js';
3
+ import { suggest } from './suggest.js';
4
+ /**
5
+ * Checks an element schema, so that it is not contradictory, and returns the problems. An empty array means that the
6
+ * schema is valid.
7
+ *
8
+ * The schema is rejected when it:
9
+ *
10
+ * * is not of the shape {@link ElementSchemaDefinition}: an object of elements, by name; has keys that are not part of
11
+ * it (typos are problems), or has values of the wrong type;
12
+ * * declares an element with an unknown `type`, or an `empty` element with `allowChildren`;
13
+ * * has attribute names that are not valid, or that differ only in case (HTML attribute names are not case sensitive),
14
+ * or an `editingTag` that is not the name of a tag;
15
+ * * refers to elements that do not exist (a name that is neither declared in the schema, nor in `external`), or lists the
16
+ * same name twice;
17
+ * * allows what its type cannot hold: text or an inline element in a container or in the root, a block-level (not inline)
18
+ * element in a block, or anything in an `empty` element, whichever side says it: `allowChildren` of the parent, or
19
+ * `allowIn` of the child.
20
+ *
21
+ * `isInline` is about the position and the layout of an element, so it is valid for every type: an inline element is
22
+ * laid out in a line with its siblings. Inline elements are allowed only in blocks, which can hold several of them,
23
+ * e.g. inline containers, or inline images between text.
24
+ *
25
+ * It does not need an editor. The editor of `ElementSchema` runs it with the elements of the other plugins as `external`.
26
+ */
27
+ export function validateElementSchema(input, { external = {} } = {}) {
28
+ const issues = [];
29
+ const report = (path, message) => {
30
+ issues.push({ path, message });
31
+ };
32
+ if (!isRecord(input)) {
33
+ report('', 'The schema must be an object of element definitions, by name: { "note": { "type": "container" } }.');
34
+ return issues;
35
+ }
36
+ const elements = input;
37
+ if (!Object.keys(elements).length) {
38
+ report('', 'The schema declares no elements: declare at least one.');
39
+ return issues;
40
+ }
41
+ if (isRecord(elements.elements) && !('type' in elements.elements)) {
42
+ report('elements', 'the schema is the object of the elements itself: remove the "elements" wrapper, and put the elements at the top.');
43
+ return issues;
44
+ }
45
+ // The first pass checks every definition on its own, and collects the kinds and the lists to cross-check.
46
+ const kinds = new Map();
47
+ const lists = new Map();
48
+ for (const [name, definition] of Object.entries(elements)) {
49
+ const path = name;
50
+ if (!ELEMENT_NAME.test(name)) {
51
+ report(path, `"${name}" is not a valid element name: use camelCase letters and digits, starting with a lowercase letter.`);
52
+ }
53
+ if (name in external) {
54
+ report(path, `"${name}" already exists in the editor (it is declared by another plugin): remove it, or rename it.`);
55
+ }
56
+ if (!isRecord(definition)) {
57
+ report(path, 'must be an object, e.g. { "type": "block" }.');
58
+ continue;
59
+ }
60
+ checkKeys(definition, ELEMENT_KEYS, path, report);
61
+ const kind = readKind(definition, path, report);
62
+ if (kind) {
63
+ kinds.set(name, kind);
64
+ }
65
+ const allowChildren = readNames(definition, 'allowChildren', path, report);
66
+ const allowIn = readNames(definition, 'allowIn', path, report);
67
+ const allowAttributes = readNames(definition, 'allowAttributes', path, report);
68
+ if (allowIn && !allowIn.length) {
69
+ report(`${path}.allowIn`, 'is empty, so the element would be allowed nowhere: list its parents, or remove it for the default places.');
70
+ }
71
+ const lowercase = new Map();
72
+ for (const [index, attribute] of (allowAttributes ?? []).entries()) {
73
+ const attributePath = `${path}.allowAttributes[${String(index)}]`;
74
+ const collides = lowercase.get(attribute.toLowerCase());
75
+ if (!ATTRIBUTE_NAME.test(attribute)) {
76
+ report(attributePath, `"${attribute}" is not a valid attribute name.`);
77
+ }
78
+ else if (collides !== undefined) {
79
+ report(attributePath, `"${attribute}" collides with "${collides}": the names of HTML attributes are not case sensitive, so the data could not tell them apart.`);
80
+ }
81
+ else {
82
+ lowercase.set(attribute.toLowerCase(), attribute);
83
+ }
84
+ }
85
+ if (definition.editingTag !== undefined &&
86
+ (typeof definition.editingTag !== 'string' || !TAG_NAME.test(definition.editingTag))) {
87
+ report(`${path}.editingTag`, `must be the name of an HTML tag, in lowercase, e.g. "section", not ${JSON.stringify(definition.editingTag)}.`);
88
+ }
89
+ if (kind?.type === 'empty' && allowChildren?.length) {
90
+ report(`${path}.allowChildren`, 'an empty element has no content: remove allowChildren, or change the type.');
91
+ }
92
+ lists.set(name, { allowChildren: kind?.type === 'empty' ? [] : (allowChildren ?? []), allowIn: allowIn ?? [] });
93
+ }
94
+ // The second pass checks what the lists refer to, and whether the types can hold each other.
95
+ const known = (name) => kinds.get(name) ?? external[name];
96
+ const candidates = [...kinds.keys(), ...Object.keys(external)];
97
+ for (const [name, { allowChildren, allowIn }] of lists) {
98
+ const kind = kinds.get(name);
99
+ if (!kind) {
100
+ continue;
101
+ }
102
+ allowChildren.forEach((child, index) => {
103
+ const path = `${name}.allowChildren[${String(index)}]`;
104
+ const childKind = child === '$text' ? child : resolveReference(child, known, candidates, path, report);
105
+ if (childKind) {
106
+ const reason = whyNot(kind, childKind);
107
+ if (reason) {
108
+ report(path, `"${child}" cannot be a child of "${name}": ${reason}.`);
109
+ }
110
+ }
111
+ });
112
+ allowIn.forEach((parent, index) => {
113
+ const path = `${name}.allowIn[${String(index)}]`;
114
+ const parentKind = parent === '$root' ? ROOT : resolveReference(parent, known, candidates, path, report);
115
+ if (parentKind) {
116
+ const reason = whyNot(parentKind, kind);
117
+ if (reason) {
118
+ report(path, `"${name}" cannot be allowed in ${parent === '$root' ? 'the root' : `"${parent}"`}: ${reason}.`);
119
+ }
120
+ }
121
+ });
122
+ }
123
+ // In the order of the file: the problems of an element together, whichever pass found them.
124
+ const names = Object.keys(elements);
125
+ const position = ({ path }) => names.findIndex(name => path === name || path.startsWith(`${name}.`));
126
+ return issues.sort((a, b) => position(a) - position(b));
127
+ }
128
+ /**
129
+ * Returns the kind of the element the list entry names. Reports an unknown name (with the closest known one), and the
130
+ * names that cannot be used there: `$text` and `$root` are not elements that can be both parents and children.
131
+ */
132
+ function resolveReference(name, known, candidates, path, report) {
133
+ if (name === '$text' || name === '$root') {
134
+ report(path, name === '$text'
135
+ ? '"$text" is not an element: it is text, and cannot have children or parents.'
136
+ : '"$root" is the root of the editor: it can be a parent (allowIn), not a child.');
137
+ return undefined;
138
+ }
139
+ const kind = known(name);
140
+ if (!kind) {
141
+ report(path, `unknown element "${name}"${suggest(name, candidates)}. It must be declared in the schema, or be an element of another plugin that is loaded.`);
142
+ }
143
+ return kind;
144
+ }
145
+ //# sourceMappingURL=validateElementSchema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validateElementSchema.js","sourceRoot":"","sources":["../src/validateElementSchema.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "ckeditor5-elements-schema",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "49.0.0-alpha.2",
4
+ "description": "Element schema for CKEditor 5: declare elements in the model schema from a simple definition, e.g. a JSON file, with three types of elements and a validation.",
5
+ "keywords": [
6
+ "ckeditor",
7
+ "ckeditor5",
8
+ "ckeditor 5",
9
+ "ckeditor5-feature",
10
+ "ckeditor5-plugin"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Ivan Kildyushov",
14
+ "homepage": "https://ivankild.github.io/ckeditor5-elements/guide/schema",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/ivankild/ckeditor5-elements.git",
18
+ "directory": "packages/ckeditor5-elements-schema"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/ivankild/ckeditor5-elements/issues"
22
+ },
23
+ "type": "module",
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js"
30
+ },
31
+ "./validate": {
32
+ "types": "./dist/validate.d.ts",
33
+ "import": "./dist/validate.js"
34
+ },
35
+ "./package.json": "./package.json"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "src",
40
+ "README.md"
41
+ ],
42
+ "sideEffects": false,
43
+ "dependencies": {
44
+ "ckeditor5-elements-core": "49.0.0-alpha.2"
45
+ },
46
+ "peerDependencies": {
47
+ "ckeditor5": "^49.0.0"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc --build"
51
+ }
6
52
  }
@@ -0,0 +1,21 @@
1
+ import type { ElementSchema, ElementSchemaInput } from './index.js';
2
+
3
+ declare module 'ckeditor5' {
4
+ interface PluginsMap {
5
+ [ElementSchema.pluginName]: ElementSchema;
6
+ }
7
+
8
+ interface EditorConfig {
9
+ /**
10
+ * The elements to declare in the model schema: an object, e.g. a JSON file. See {@link ElementSchema}.
11
+ */
12
+ elementSchema?: ElementSchemaInput;
13
+
14
+ /**
15
+ * Whether to declare the converters of the elements of `elementSchema`, one to one: `div` or `span` with the name as
16
+ * the class, and the attributes as `data-*` attributes, in the data and in the editing view; `editingTag` of an
17
+ * element sets another tag for the editing view. See {@link ElementSchema}. Default: `false`.
18
+ */
19
+ elementSchemaConverters?: boolean;
20
+ }
21
+ }
package/src/canHold.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { ElementSchemaKind } from './types.js';
2
+
3
+ /**
4
+ * The root can hold blocks, containers and block-level empty elements, like a container.
5
+ */
6
+ export const ROOT: ElementSchemaKind = { type: 'container', isInline: false };
7
+
8
+ /**
9
+ * Returns why `parent` cannot contain `child` (an element, or `'$text'`), or `null` when it can.
10
+ */
11
+ export function whyNot(parent: ElementSchemaKind, child: ElementSchemaKind | '$text'): string | null {
12
+ const isText = child === '$text';
13
+
14
+ if (parent.type === 'empty') {
15
+ return 'an empty element cannot contain anything';
16
+ }
17
+
18
+ if (parent.type === 'block') {
19
+ const what = parent.isInline ? 'an inline text element' : 'a block';
20
+
21
+ return isText || child.isInline ? null : `${what} can contain only text and inline elements`;
22
+ }
23
+
24
+ if (isText) {
25
+ return 'a container cannot contain text: put the text in a block';
26
+ }
27
+
28
+ return child.isInline ? 'a container cannot contain inline elements: put them in a block' : null;
29
+ }
@@ -0,0 +1,88 @@
1
+ import type { ModelSchemaItemDefinition } from 'ckeditor5';
2
+ import type { ElementSchemaElement, ElementSchemaInput } from './types.js';
3
+
4
+ /**
5
+ * The result of {@link compileElementSchema}.
6
+ */
7
+ export interface CompiledElementSchema {
8
+ /**
9
+ * The items to register in CKEditor's schema, by name.
10
+ */
11
+ items: [name: string, definition: ModelSchemaItemDefinition][];
12
+
13
+ /**
14
+ * The `allowChildren` of the elements, as the places to add to the children: `schema.extend(child, { allowIn: parent })`.
15
+ * The child can be an element of another plugin, which is extended, not registered.
16
+ */
17
+ children: { parent: string; child: string }[];
18
+ }
19
+
20
+ /**
21
+ * Translates a valid element schema to the definitions of CKEditor's schema. Each type is made like a generic item of
22
+ * CKEditor:
23
+ *
24
+ * * `container`: like `$container`: allowed in the root and in containers; holds what is allowed in a container: blocks,
25
+ * containers and block objects, not text.
26
+ * * `block`: like `$block`: a block, allowed in the root and in containers; holds text and inline objects.
27
+ * * `empty` and not inline: like `$blockObject`: an object, allowed where blocks are.
28
+ * * `empty` and inline: like `$inlineObject`: an object, allowed where text is, with the attributes of text.
29
+ * * `block` and inline: an inline element that holds text and inline elements, allowed where text is, with the attributes
30
+ * of text: like `$text`, but an element, so it can have attributes of its own.
31
+ * * `container` and inline: a container laid out in a line, e.g. with others in a block: allowed where text is, like every
32
+ * inline element, and holds the same as a container: blocks, not text.
33
+ *
34
+ * `isInline` is about the layout and the position of an element, so it is set for every type, and CKEditor's features and
35
+ * the converters can use it.
36
+ *
37
+ * `allowAttributes` is passed as it is. `allowIn` replaces the default places of the type. `allowChildren` adds to what
38
+ * the type holds, as in CKEditor.
39
+ */
40
+ export function compileElementSchema(input: ElementSchemaInput): CompiledElementSchema {
41
+ const items: CompiledElementSchema['items'] = [];
42
+ const children: CompiledElementSchema['children'] = [];
43
+
44
+ for (const [name, element] of Object.entries(input)) {
45
+ items.push([name, compileElement(element as ElementSchemaElement)]);
46
+
47
+ for (const child of element.allowChildren ?? []) {
48
+ children.push({ parent: name, child });
49
+ }
50
+ }
51
+
52
+ return { items, children };
53
+ }
54
+
55
+ function compileElement({ type, isInline, allowIn, allowAttributes }: ElementSchemaElement): ModelSchemaItemDefinition {
56
+ // Where the element is allowed: the places of its type, or the given ones.
57
+ const places = (defaultPlaces: string): Pick<ModelSchemaItemDefinition, 'allowIn' | 'allowWhere'> =>
58
+ allowIn ? { allowIn } : { allowWhere: defaultPlaces };
59
+ const attributes = allowAttributes ? { allowAttributes } : {};
60
+
61
+ if (type === 'empty' && isInline) {
62
+ return { inheritTypesFrom: '$inlineObject', allowAttributesOf: '$text', ...places('$text'), ...attributes };
63
+ }
64
+
65
+ if (type === 'empty') {
66
+ return { inheritTypesFrom: '$blockObject', ...places('$block'), ...attributes };
67
+ }
68
+
69
+ if (type === 'block' && isInline) {
70
+ return {
71
+ isInline: true,
72
+ allowContentOf: '$block',
73
+ allowAttributesOf: '$text',
74
+ ...places('$text'),
75
+ ...attributes
76
+ };
77
+ }
78
+
79
+ if (type === 'block') {
80
+ return { inheritTypesFrom: '$block', allowContentOf: '$block', ...places('$block'), ...attributes };
81
+ }
82
+
83
+ return {
84
+ allowContentOf: '$container',
85
+ ...(isInline ? { isInline: true, ...places('$text') } : places('$block')),
86
+ ...attributes
87
+ };
88
+ }