Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Socket
Sign inDemoInstall

@atlaskit/adf-schema-generator

Package Overview
Dependencies
Maintainers
2
Versions
82
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@atlaskit/adf-schema-generator - npm Package Compare versions

Comparing version 1.17.4 to 1.17.5

4

CHANGELOG.md
# @atlaskit/adf-schema-generator
## 1.17.4
## 1.17.5
### Patch Changes
- 9789fe6: Alphabetically sort imports in Generated PM Spec files
- 2a8c66a: Add allOf field to JSON transformer from ADF DSL

@@ -73,2 +73,12 @@ "use strict";

/**
* If true, the node doesn't allow marks to be set.
* This is stricter than simply having an empty marks list.
*/
}, {
key: "hasNoMarks",
value: function hasNoMarks() {
return _classPrivateFieldGet(_spec, this).noMarks;
}
/**
* Define a node.

@@ -81,3 +91,3 @@ *

value: function define(spec) {
var _spec$marks$map, _spec$marks;
var _spec$marks, _spec$marks$map, _spec$marks2;
if (_classPrivateFieldGet(_spec, this)) {

@@ -87,3 +97,6 @@ throw new Error('Cannot re-define a node');

_classPrivateFieldSet(_spec, this, spec);
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.map(function (mark) {
if (spec.noMarks && ((_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.length) > 0) {
throw new Error('Node with noMarks true has marks');
}
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks2 = spec.marks) === null || _spec$marks2 === void 0 ? void 0 : _spec$marks2.map(function (mark) {
return mark.getType();

@@ -90,0 +103,0 @@ })) !== null && _spec$marks$map !== void 0 ? _spec$marks$map : []);

@@ -8,59 +8,95 @@ "use strict";

exports.buildContent = buildContent;
exports.isADFNode = isADFNode;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _uniqBy = _interopRequireDefault(require("lodash/uniqBy"));
var _adfNode = require("../../adfNode");
var _transformerNames = require("../transformerNames");
var _inconsistentNameResolver = require("./inconsistentNameResolver");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function buildContent(content, name) {
function buildContent(content, name, adfNodeContent) {
if (content.length === 0) return {};
var minItems = determineMinItems(content, name);
var items = determineItems(content, name);
var items = determineItems(content, name, adfNodeContent);
var maxItems = determineMaxItems(content, adfNodeContent);
return {
content: _objectSpread({
content: _objectSpread(_objectSpread({
type: 'array',
items: items
}, minItems)
}, minItems !== null ? {
minItems: minItems
} : {}), maxItems !== null ? {
maxItems: maxItems
} : {})
};
}
function determineItems(content, name) {
// These are arrays instead of objects in the JSON Schema
if (name === 'listItem' || name === 'taskList') {
return handleListContent(content);
}
// TODO: codeblock is insane
function isADFGroup(value) {
return value && 'group' in value;
}
function isADFNode(value) {
return value && value instanceof _adfNode.ADFNode;
}
function determineItems(content, name, adfNodeContent) {
// TODO: codeblock has a variant pattern instead of a normal node pattern
// This is probably an expanded reference to the inline code node, a variant of text node
// To remove this exception, we must update the JSON Schema to use a reference here instead
if (name === 'codeBlock') {
return {};
return {
allOf: [{
$ref: '#/definitions/text_node'
}, {
properties: {
marks: {
maxItems: 0,
type: 'array'
}
},
type: 'object'
}]
};
}
// If there is one piece of content, defined as $zeroPlus($or([content])) or $onePlus($or([content])), then it's a single $ref in an object
if (content.length === 1 && content[0].contentTypes.length === 1) {
if (name === 'doc') {
var jsonSchema = adfNodeContent.reduce(function (acc, value) {
// We're expecting a single one+ with a nested $or
if (value.type === '$one+' && value.content.type === '$or') {
return [].concat((0, _toConsumableArray2.default)(acc), (0, _toConsumableArray2.default)(flattenContent(value.content.content).map(function (node) {
return {
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(node.getName()))
};
})));
}
return acc;
}, []);
return {
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(content[0].contentTypes[0]))
anyOf: (0, _uniqBy.default)(jsonSchema, function (v) {
return v.$ref;
})
};
}
if (content.length === 1 && content[0].contentTypes.length > 1) {
return handleObjectContent(content[0]);
}
return {};
var processedContentGroups = processContentGroups(content);
// The JSON schema omits the array if there is only 1 item
return flattenArray(processedContentGroups);
}
function handleObjectContent(content) {
var itemsArray = content.contentTypes.map(function (piece) {
function processContentTypes(contentTypes) {
var itemsArray = [];
contentTypes.forEach(function (piece) {
itemsArray.push({
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(piece))
});
});
// We flatten an array here as well, but using anyOf to fit the JSON schema
if (itemsArray.length === 1) {
return itemsArray[0];
} else {
return {
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(piece))
anyOf: itemsArray
};
});
return {
anyOf: itemsArray
};
}
}
function handleListContent(content) {
function processContentGroups(content) {
var contentArray = [];
content.forEach(function (item) {
if (item.contentTypes.length === 1) {
contentArray.push({
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(item.contentTypes[0]))
});
} else if (item.contentTypes.length > 1) {
contentArray.push(handleObjectContent(item));
}
contentArray.push(processContentTypes(item.contentTypes));
});

@@ -70,18 +106,79 @@ return contentArray;

function determineMinItems(content, name) {
var minItems;
content.forEach(function (value) {
// Despite being oneplus, tableRow and doc have no minItems field
if (value.minItems === 1 && name !== 'tableRow' && name !== 'doc') {
minItems = {
minItems: 1
};
var _content$find, _content$find2;
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one minItem value for all content on a node.
if (!content) return null;
var _ref = (_content$find = content.find(function (v) {
return !isNaN(v.minItems);
})) !== null && _content$find !== void 0 ? _content$find : {},
minItems = _ref.minItems;
var _ref2 = (_content$find2 = content.find(function (v) {
return v.range;
})) !== null && _content$find2 !== void 0 ? _content$find2 : {},
range = _ref2.range;
// Despite being oneplus, tableRow and doc have no minItems field
if (minItems === 1 && name !== 'tableRow' && name !== 'doc' && name !== 'layoutSection') {
return minItems;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
if (minItems === 0 && name === 'caption') {
return minItems;
}
// If it's a range, take minItems from that
if (range) {
return range.min;
}
return null;
}
// This function is not comprehensive, it is only defined for certain inputs
function determineMaxItems(content, adfNodeContent) {
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one maxItem value for all content on a node.
// If there's only one item, we can simply calculate it and return
if (content.length === 1) {
// If it's a range, grab maxItems from that
if (content[0].range) {
return content[0].range.max;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
else if (name === 'caption' && value.minItems === 0) {
minItems = {
minItems: 0
};
} else if (adfNodeContent.length > 1) {
var types = adfNodeContent.map(function (v) {
return v.type;
});
if (types.includes('$one+') || types.includes('$zero+')) {
return null;
}
});
return minItems;
var maxItems = types.filter(function (v) {
return v === '$or';
}).length;
return maxItems || null;
}
return null;
}
function flattenArray(array) {
if (array.length === 1) {
return array[0];
} else {
return array;
}
}
/**
* Flattens ADF groups and nodes into an array of nodes.
* @param content
* @returns ADFNode[]
*/
function flattenContent(content) {
return content.reduce(function (acc, item) {
if (isADFGroup(item)) {
// Expand the group into its member nodes
return [].concat((0, _toConsumableArray2.default)(acc), (0, _toConsumableArray2.default)(flattenContent(item.members)));
} else if (isADFNode(item) && !item.isIgnored(_transformerNames.JSONSchemaTransformerName)) {
return [].concat((0, _toConsumableArray2.default)(acc), [item]);
}
return acc;
}, []);
}

@@ -13,2 +13,3 @@ "use strict";

var _contentBuilder = require("./contentBuilder");
var _inconsistentNameResolver = require("./inconsistentNameResolver");
var _requiredBuilder = require("./requiredBuilder");

@@ -26,3 +27,3 @@ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }

}).filter(Boolean)) || [];
var marks = buildNodeMarks(nodeMarks);
var marks = buildNodeMarks(nodeMarks, node.hasNoMarks());
var attrs = (0, _attrBuilder.buildAttrs)(node.getSpec().attrs);

@@ -36,3 +37,3 @@ // Strangely, text has this extra property that looks like an attribute

} : {};
var jsonContent = (0, _contentBuilder.buildContent)(content, node.getName());
var jsonContent = (0, _contentBuilder.buildContent)(content, node.getName(), node.getSpec().content);
var version = node.getSpec().version;

@@ -55,6 +56,31 @@ var jsonVersion = version ? {

json.required = required;
} else {
var _required = (0, _requiredBuilder.buildVariantRequired)(content);
var allOfItem = _objectSpread({
type: 'object',
properties: _objectSpread(_objectSpread({}, marks), jsonContent)
}, _required);
if (node.getName() === 'layoutSection_full') {
// Only on the layoutSection_full variant we specify type
allOfItem.properties.type = {
enum: ['layoutSection']
};
allOfItem.required = ['type', 'content'];
allOfItem.additionalProperties = false;
}
json.allOf = [{
$ref: "#/definitions/".concat((0, _inconsistentNameResolver.resolveName)(node.getBase().getName()))
}, allOfItem];
}
return json;
};
function buildNodeMarks(nodeMarks) {
function buildNodeMarks(nodeMarks, hasNoMarks) {
if (hasNoMarks) {
return {
marks: {
type: 'array',
maxItems: 0
}
};
}
if (nodeMarks.length === 0) return {};

@@ -61,0 +87,0 @@ if (nodeMarks.length === 1) return {

@@ -7,2 +7,3 @@ "use strict";

exports.buildRequired = buildRequired;
exports.buildVariantRequired = buildVariantRequired;
var _isAnyOf = require("../../utils/isAnyOf");

@@ -57,2 +58,11 @@ function buildRequired(attrs, hasContent, name) {

return required;
}
function buildVariantRequired(content) {
var _content$, _content$2, _content$2$range;
if (((_content$ = content[0]) === null || _content$ === void 0 ? void 0 : _content$.minItems) >= 1 || Boolean((_content$2 = content[0]) === null || _content$2 === void 0 ? void 0 : (_content$2$range = _content$2.range) === null || _content$2$range === void 0 ? void 0 : _content$2$range.type)) {
return {
required: ['content']
};
}
return {};
}

@@ -33,3 +33,5 @@ "use strict";

if (_node.isIgnored(_transformerNames.PMSpecTransformerName)) {
return;
return {
node: _node
};
}

@@ -56,8 +58,12 @@ var marks = (_node$getSpec$marks = _node.getSpec().marks) !== null && _node$getSpec$marks !== void 0 ? _node$getSpec$marks : [];

group: function group(_group, members) {
nodeGroupMap[_group.group] = _group.members.map(function (m) {
return m.getName();
nodeGroupMap[_group.group] = _group.members.filter(function (node) {
return !node.isIgnored(_transformerNames.PMSpecTransformerName);
}).map(function (node) {
return node.getName();
});
return {
group: _group.group,
members: members
members: members.filter(function (m) {
return !m.node.isIgnored(_transformerNames.PMSpecTransformerName);
})
};

@@ -64,0 +70,0 @@ },

@@ -53,2 +53,10 @@ function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }

/**
* If true, the node doesn't allow marks to be set.
* This is stricter than simply having an empty marks list.
*/
hasNoMarks() {
return _classPrivateFieldGet(_spec, this).noMarks;
}
/**
* Define a node.

@@ -59,3 +67,3 @@ *

define(spec) {
var _spec$marks$map, _spec$marks;
var _spec$marks, _spec$marks$map, _spec$marks2;
if (_classPrivateFieldGet(_spec, this)) {

@@ -65,3 +73,6 @@ throw new Error('Cannot re-define a node');

_classPrivateFieldSet(_spec, this, spec);
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.map(mark => mark.getType())) !== null && _spec$marks$map !== void 0 ? _spec$marks$map : []);
if (spec.noMarks && ((_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.length) > 0) {
throw new Error('Node with noMarks true has marks');
}
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks2 = spec.marks) === null || _spec$marks2 === void 0 ? void 0 : _spec$marks2.map(mark => mark.getType())) !== null && _spec$marks$map !== void 0 ? _spec$marks$map : []);
return this;

@@ -68,0 +79,0 @@ }

@@ -0,6 +1,10 @@

import uniqBy from 'lodash/uniqBy';
import { ADFNode } from '../../adfNode';
import { JSONSchemaTransformerName } from '../transformerNames';
import { resolveName } from './inconsistentNameResolver';
export function buildContent(content, name) {
export function buildContent(content, name, adfNodeContent) {
if (content.length === 0) return {};
const minItems = determineMinItems(content, name);
const items = determineItems(content, name);
const items = determineItems(content, name, adfNodeContent);
const maxItems = determineMaxItems(content, adfNodeContent);
return {

@@ -10,48 +14,75 @@ content: {

items,
...minItems
...(minItems !== null ? {
minItems
} : {}),
...(maxItems !== null ? {
maxItems
} : {})
}
};
}
function determineItems(content, name) {
// These are arrays instead of objects in the JSON Schema
if (name === 'listItem' || name === 'taskList') {
return handleListContent(content);
}
// TODO: codeblock is insane
function isADFGroup(value) {
return value && 'group' in value;
}
export function isADFNode(value) {
return value && value instanceof ADFNode;
}
function determineItems(content, name, adfNodeContent) {
// TODO: codeblock has a variant pattern instead of a normal node pattern
// This is probably an expanded reference to the inline code node, a variant of text node
// To remove this exception, we must update the JSON Schema to use a reference here instead
if (name === 'codeBlock') {
return {};
return {
allOf: [{
$ref: '#/definitions/text_node'
}, {
properties: {
marks: {
maxItems: 0,
type: 'array'
}
},
type: 'object'
}]
};
}
// If there is one piece of content, defined as $zeroPlus($or([content])) or $onePlus($or([content])), then it's a single $ref in an object
if (content.length === 1 && content[0].contentTypes.length === 1) {
if (name === 'doc') {
const jsonSchema = adfNodeContent.reduce((acc, value) => {
// We're expecting a single one+ with a nested $or
if (value.type === '$one+' && value.content.type === '$or') {
return [...acc, ...flattenContent(value.content.content).map(node => ({
$ref: `#/definitions/${resolveName(node.getName())}`
}))];
}
return acc;
}, []);
return {
$ref: `#/definitions/${resolveName(content[0].contentTypes[0])}`
anyOf: uniqBy(jsonSchema, v => v.$ref)
};
}
if (content.length === 1 && content[0].contentTypes.length > 1) {
return handleObjectContent(content[0]);
}
return {};
const processedContentGroups = processContentGroups(content);
// The JSON schema omits the array if there is only 1 item
return flattenArray(processedContentGroups);
}
function handleObjectContent(content) {
const itemsArray = content.contentTypes.map(piece => {
function processContentTypes(contentTypes) {
const itemsArray = [];
contentTypes.forEach(piece => {
itemsArray.push({
$ref: `#/definitions/${resolveName(piece)}`
});
});
// We flatten an array here as well, but using anyOf to fit the JSON schema
if (itemsArray.length === 1) {
return itemsArray[0];
} else {
return {
$ref: `#/definitions/${resolveName(piece)}`
anyOf: itemsArray
};
});
return {
anyOf: itemsArray
};
}
}
function handleListContent(content) {
function processContentGroups(content) {
const contentArray = [];
content.forEach(item => {
if (item.contentTypes.length === 1) {
contentArray.push({
$ref: `#/definitions/${resolveName(item.contentTypes[0])}`
});
} else if (item.contentTypes.length > 1) {
contentArray.push(handleObjectContent(item));
}
contentArray.push(processContentTypes(item.contentTypes));
});

@@ -61,18 +92,73 @@ return contentArray;

function determineMinItems(content, name) {
let minItems;
content.forEach(value => {
// Despite being oneplus, tableRow and doc have no minItems field
if (value.minItems === 1 && name !== 'tableRow' && name !== 'doc') {
minItems = {
minItems: 1
};
var _content$find, _content$find2;
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one minItem value for all content on a node.
if (!content) return null;
const {
minItems
} = (_content$find = content.find(v => !isNaN(v.minItems))) !== null && _content$find !== void 0 ? _content$find : {};
const {
range
} = (_content$find2 = content.find(v => v.range)) !== null && _content$find2 !== void 0 ? _content$find2 : {};
// Despite being oneplus, tableRow and doc have no minItems field
if (minItems === 1 && name !== 'tableRow' && name !== 'doc' && name !== 'layoutSection') {
return minItems;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
if (minItems === 0 && name === 'caption') {
return minItems;
}
// If it's a range, take minItems from that
if (range) {
return range.min;
}
return null;
}
// This function is not comprehensive, it is only defined for certain inputs
function determineMaxItems(content, adfNodeContent) {
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one maxItem value for all content on a node.
// If there's only one item, we can simply calculate it and return
if (content.length === 1) {
// If it's a range, grab maxItems from that
if (content[0].range) {
return content[0].range.max;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
else if (name === 'caption' && value.minItems === 0) {
minItems = {
minItems: 0
};
} else if (adfNodeContent.length > 1) {
const types = adfNodeContent.map(v => v.type);
if (types.includes('$one+') || types.includes('$zero+')) {
return null;
}
});
return minItems;
const maxItems = types.filter(v => v === '$or').length;
return maxItems || null;
}
return null;
}
function flattenArray(array) {
if (array.length === 1) {
return array[0];
} else {
return array;
}
}
/**
* Flattens ADF groups and nodes into an array of nodes.
* @param content
* @returns ADFNode[]
*/
function flattenContent(content) {
return content.reduce((acc, item) => {
if (isADFGroup(item)) {
// Expand the group into its member nodes
return [...acc, ...flattenContent(item.members)];
} else if (isADFNode(item) && !item.isIgnored(JSONSchemaTransformerName)) {
return [...acc, item];
}
return acc;
}, []);
}

@@ -5,3 +5,4 @@ import { JSONSchemaTransformerName } from '../transformerNames';

import { buildContent } from './contentBuilder';
import { buildRequired } from './requiredBuilder';
import { resolveName } from './inconsistentNameResolver';
import { buildRequired, buildVariantRequired } from './requiredBuilder';
export const buildNode = (node, content) => {

@@ -16,3 +17,3 @@ var _node$getSpec$marks, _content$, _content$2, _content$2$range;

}).filter(Boolean)) || [];
const marks = buildNodeMarks(nodeMarks);
const marks = buildNodeMarks(nodeMarks, node.hasNoMarks());
const attrs = buildAttrs(node.getSpec().attrs);

@@ -26,3 +27,3 @@ // Strangely, text has this extra property that looks like an attribute

} : {};
const jsonContent = buildContent(content, node.getName());
const jsonContent = buildContent(content, node.getName(), node.getSpec().content);
const version = node.getSpec().version;

@@ -50,6 +51,35 @@ const jsonVersion = version ? {

json.required = required;
} else {
const required = buildVariantRequired(content);
const allOfItem = {
type: 'object',
properties: {
...marks,
...jsonContent
},
...required
};
if (node.getName() === 'layoutSection_full') {
// Only on the layoutSection_full variant we specify type
allOfItem.properties.type = {
enum: ['layoutSection']
};
allOfItem.required = ['type', 'content'];
allOfItem.additionalProperties = false;
}
json.allOf = [{
$ref: `#/definitions/${resolveName(node.getBase().getName())}`
}, allOfItem];
}
return json;
};
function buildNodeMarks(nodeMarks) {
function buildNodeMarks(nodeMarks, hasNoMarks) {
if (hasNoMarks) {
return {
marks: {
type: 'array',
maxItems: 0
}
};
}
if (nodeMarks.length === 0) return {};

@@ -56,0 +86,0 @@ if (nodeMarks.length === 1) return {

@@ -50,2 +50,11 @@ import { isAnyOf } from '../../utils/isAnyOf';

return required;
}
export function buildVariantRequired(content) {
var _content$, _content$2, _content$2$range;
if (((_content$ = content[0]) === null || _content$ === void 0 ? void 0 : _content$.minItems) >= 1 || Boolean((_content$2 = content[0]) === null || _content$2 === void 0 ? void 0 : (_content$2$range = _content$2.range) === null || _content$2$range === void 0 ? void 0 : _content$2$range.type)) {
return {
required: ['content']
};
}
return {};
}

@@ -23,3 +23,5 @@ import { traverse } from '../../traverse';

if (node.isIgnored(PMSpecTransformerName)) {
return;
return {
node
};
}

@@ -46,6 +48,6 @@ const marks = (_node$getSpec$marks = node.getSpec().marks) !== null && _node$getSpec$marks !== void 0 ? _node$getSpec$marks : [];

group: (group, members) => {
nodeGroupMap[group.group] = group.members.map(m => m.getName());
nodeGroupMap[group.group] = group.members.filter(node => !node.isIgnored(PMSpecTransformerName)).map(node => node.getName());
return {
group: group.group,
members
members: members.filter(m => !m.node.isIgnored(PMSpecTransformerName))
};

@@ -52,0 +54,0 @@ },

@@ -65,2 +65,12 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";

/**
* If true, the node doesn't allow marks to be set.
* This is stricter than simply having an empty marks list.
*/
}, {
key: "hasNoMarks",
value: function hasNoMarks() {
return _classPrivateFieldGet(_spec, this).noMarks;
}
/**
* Define a node.

@@ -73,3 +83,3 @@ *

value: function define(spec) {
var _spec$marks$map, _spec$marks;
var _spec$marks, _spec$marks$map, _spec$marks2;
if (_classPrivateFieldGet(_spec, this)) {

@@ -79,3 +89,6 @@ throw new Error('Cannot re-define a node');

_classPrivateFieldSet(_spec, this, spec);
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.map(function (mark) {
if (spec.noMarks && ((_spec$marks = spec.marks) === null || _spec$marks === void 0 ? void 0 : _spec$marks.length) > 0) {
throw new Error('Node with noMarks true has marks');
}
_classPrivateFieldSet(_marks, this, (_spec$marks$map = (_spec$marks2 = spec.marks) === null || _spec$marks2 === void 0 ? void 0 : _spec$marks2.map(function (mark) {
return mark.getType();

@@ -82,0 +95,0 @@ })) !== null && _spec$marks$map !== void 0 ? _spec$marks$map : []);

@@ -0,58 +1,93 @@

import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import uniqBy from 'lodash/uniqBy';
import { ADFNode } from '../../adfNode';
import { JSONSchemaTransformerName } from '../transformerNames';
import { resolveName } from './inconsistentNameResolver';
export function buildContent(content, name) {
export function buildContent(content, name, adfNodeContent) {
if (content.length === 0) return {};
var minItems = determineMinItems(content, name);
var items = determineItems(content, name);
var items = determineItems(content, name, adfNodeContent);
var maxItems = determineMaxItems(content, adfNodeContent);
return {
content: _objectSpread({
content: _objectSpread(_objectSpread({
type: 'array',
items: items
}, minItems)
}, minItems !== null ? {
minItems: minItems
} : {}), maxItems !== null ? {
maxItems: maxItems
} : {})
};
}
function determineItems(content, name) {
// These are arrays instead of objects in the JSON Schema
if (name === 'listItem' || name === 'taskList') {
return handleListContent(content);
}
// TODO: codeblock is insane
function isADFGroup(value) {
return value && 'group' in value;
}
export function isADFNode(value) {
return value && value instanceof ADFNode;
}
function determineItems(content, name, adfNodeContent) {
// TODO: codeblock has a variant pattern instead of a normal node pattern
// This is probably an expanded reference to the inline code node, a variant of text node
// To remove this exception, we must update the JSON Schema to use a reference here instead
if (name === 'codeBlock') {
return {};
return {
allOf: [{
$ref: '#/definitions/text_node'
}, {
properties: {
marks: {
maxItems: 0,
type: 'array'
}
},
type: 'object'
}]
};
}
// If there is one piece of content, defined as $zeroPlus($or([content])) or $onePlus($or([content])), then it's a single $ref in an object
if (content.length === 1 && content[0].contentTypes.length === 1) {
if (name === 'doc') {
var jsonSchema = adfNodeContent.reduce(function (acc, value) {
// We're expecting a single one+ with a nested $or
if (value.type === '$one+' && value.content.type === '$or') {
return [].concat(_toConsumableArray(acc), _toConsumableArray(flattenContent(value.content.content).map(function (node) {
return {
$ref: "#/definitions/".concat(resolveName(node.getName()))
};
})));
}
return acc;
}, []);
return {
$ref: "#/definitions/".concat(resolveName(content[0].contentTypes[0]))
anyOf: uniqBy(jsonSchema, function (v) {
return v.$ref;
})
};
}
if (content.length === 1 && content[0].contentTypes.length > 1) {
return handleObjectContent(content[0]);
}
return {};
var processedContentGroups = processContentGroups(content);
// The JSON schema omits the array if there is only 1 item
return flattenArray(processedContentGroups);
}
function handleObjectContent(content) {
var itemsArray = content.contentTypes.map(function (piece) {
function processContentTypes(contentTypes) {
var itemsArray = [];
contentTypes.forEach(function (piece) {
itemsArray.push({
$ref: "#/definitions/".concat(resolveName(piece))
});
});
// We flatten an array here as well, but using anyOf to fit the JSON schema
if (itemsArray.length === 1) {
return itemsArray[0];
} else {
return {
$ref: "#/definitions/".concat(resolveName(piece))
anyOf: itemsArray
};
});
return {
anyOf: itemsArray
};
}
}
function handleListContent(content) {
function processContentGroups(content) {
var contentArray = [];
content.forEach(function (item) {
if (item.contentTypes.length === 1) {
contentArray.push({
$ref: "#/definitions/".concat(resolveName(item.contentTypes[0]))
});
} else if (item.contentTypes.length > 1) {
contentArray.push(handleObjectContent(item));
}
contentArray.push(processContentTypes(item.contentTypes));
});

@@ -62,18 +97,79 @@ return contentArray;

function determineMinItems(content, name) {
var minItems;
content.forEach(function (value) {
// Despite being oneplus, tableRow and doc have no minItems field
if (value.minItems === 1 && name !== 'tableRow' && name !== 'doc') {
minItems = {
minItems: 1
};
var _content$find, _content$find2;
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one minItem value for all content on a node.
if (!content) return null;
var _ref = (_content$find = content.find(function (v) {
return !isNaN(v.minItems);
})) !== null && _content$find !== void 0 ? _content$find : {},
minItems = _ref.minItems;
var _ref2 = (_content$find2 = content.find(function (v) {
return v.range;
})) !== null && _content$find2 !== void 0 ? _content$find2 : {},
range = _ref2.range;
// Despite being oneplus, tableRow and doc have no minItems field
if (minItems === 1 && name !== 'tableRow' && name !== 'doc' && name !== 'layoutSection') {
return minItems;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
if (minItems === 0 && name === 'caption') {
return minItems;
}
// If it's a range, take minItems from that
if (range) {
return range.min;
}
return null;
}
// This function is not comprehensive, it is only defined for certain inputs
function determineMaxItems(content, adfNodeContent) {
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one maxItem value for all content on a node.
// If there's only one item, we can simply calculate it and return
if (content.length === 1) {
// If it's a range, grab maxItems from that
if (content[0].range) {
return content[0].range.max;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
else if (name === 'caption' && value.minItems === 0) {
minItems = {
minItems: 0
};
} else if (adfNodeContent.length > 1) {
var types = adfNodeContent.map(function (v) {
return v.type;
});
if (types.includes('$one+') || types.includes('$zero+')) {
return null;
}
});
return minItems;
var maxItems = types.filter(function (v) {
return v === '$or';
}).length;
return maxItems || null;
}
return null;
}
function flattenArray(array) {
if (array.length === 1) {
return array[0];
} else {
return array;
}
}
/**
* Flattens ADF groups and nodes into an array of nodes.
* @param content
* @returns ADFNode[]
*/
function flattenContent(content) {
return content.reduce(function (acc, item) {
if (isADFGroup(item)) {
// Expand the group into its member nodes
return [].concat(_toConsumableArray(acc), _toConsumableArray(flattenContent(item.members)));
} else if (isADFNode(item) && !item.isIgnored(JSONSchemaTransformerName)) {
return [].concat(_toConsumableArray(acc), [item]);
}
return acc;
}, []);
}

@@ -8,3 +8,4 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";

import { buildContent } from './contentBuilder';
import { buildRequired } from './requiredBuilder';
import { resolveName } from './inconsistentNameResolver';
import { buildRequired, buildVariantRequired } from './requiredBuilder';
export var buildNode = function buildNode(node, content) {

@@ -19,3 +20,3 @@ var _node$getSpec$marks, _content$, _content$2, _content$2$range;

}).filter(Boolean)) || [];
var marks = buildNodeMarks(nodeMarks);
var marks = buildNodeMarks(nodeMarks, node.hasNoMarks());
var attrs = buildAttrs(node.getSpec().attrs);

@@ -29,3 +30,3 @@ // Strangely, text has this extra property that looks like an attribute

} : {};
var jsonContent = buildContent(content, node.getName());
var jsonContent = buildContent(content, node.getName(), node.getSpec().content);
var version = node.getSpec().version;

@@ -48,6 +49,31 @@ var jsonVersion = version ? {

json.required = required;
} else {
var _required = buildVariantRequired(content);
var allOfItem = _objectSpread({
type: 'object',
properties: _objectSpread(_objectSpread({}, marks), jsonContent)
}, _required);
if (node.getName() === 'layoutSection_full') {
// Only on the layoutSection_full variant we specify type
allOfItem.properties.type = {
enum: ['layoutSection']
};
allOfItem.required = ['type', 'content'];
allOfItem.additionalProperties = false;
}
json.allOf = [{
$ref: "#/definitions/".concat(resolveName(node.getBase().getName()))
}, allOfItem];
}
return json;
};
function buildNodeMarks(nodeMarks) {
function buildNodeMarks(nodeMarks, hasNoMarks) {
if (hasNoMarks) {
return {
marks: {
type: 'array',
maxItems: 0
}
};
}
if (nodeMarks.length === 0) return {};

@@ -54,0 +80,0 @@ if (nodeMarks.length === 1) return {

@@ -50,2 +50,11 @@ import { isAnyOf } from '../../utils/isAnyOf';

return required;
}
export function buildVariantRequired(content) {
var _content$, _content$2, _content$2$range;
if (((_content$ = content[0]) === null || _content$ === void 0 ? void 0 : _content$.minItems) >= 1 || Boolean((_content$2 = content[0]) === null || _content$2 === void 0 ? void 0 : (_content$2$range = _content$2.range) === null || _content$2$range === void 0 ? void 0 : _content$2$range.type)) {
return {
required: ['content']
};
}
return {};
}

@@ -26,3 +26,5 @@ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }

if (_node.isIgnored(PMSpecTransformerName)) {
return;
return {
node: _node
};
}

@@ -49,8 +51,12 @@ var marks = (_node$getSpec$marks = _node.getSpec().marks) !== null && _node$getSpec$marks !== void 0 ? _node$getSpec$marks : [];

group: function group(_group, members) {
nodeGroupMap[_group.group] = _group.members.map(function (m) {
return m.getName();
nodeGroupMap[_group.group] = _group.members.filter(function (node) {
return !node.isIgnored(PMSpecTransformerName);
}).map(function (node) {
return node.getName();
});
return {
group: _group.group,
members: members
members: members.filter(function (m) {
return !m.node.isIgnored(PMSpecTransformerName);
})
};

@@ -57,0 +63,0 @@ },

@@ -20,2 +20,7 @@ import type { TransformerNames } from './transforms/transformerNames';

/**
* If true, the node doesn't allow marks to be set.
* This is stricter than simply having an empty marks list.
*/
hasNoMarks(): boolean;
/**
* Define a node.

@@ -22,0 +27,0 @@ *

import type { ADFNode } from '../../adfNode';
export declare function adfToJSON(adf: ADFNode<[string], any>): Record<string, any>;
import type { JSONSchema4 } from 'json-schema';
export declare function adfToJSON(adf: ADFNode<[string], any>): JSONSchema4;

@@ -0,9 +1,14 @@

import { JSONSchema4 } from 'json-schema-types';
import { ADFNode } from '../../adfNode';
import { ADFNodeGroup } from '../../types/ADFNodeGroup';
import { ADFNodeContentSpec } from '../../types/ADFNodeSpec';
import { ContentVisitorReturnType } from './adfToJsonVisitor';
export declare function buildContent(content: Array<ContentVisitorReturnType>, name: string): {
export declare function buildContent(content: Array<ContentVisitorReturnType>, name: string, adfNodeContent: Array<ADFNodeContentSpec>): {
content?: undefined;
} | {
content: {
minItems: 0 | 1;
maxItems?: number;
minItems?: number;
type: string;
items: ({
items: {
anyOf: {

@@ -14,3 +19,3 @@ $ref: string;

$ref: string;
})[] | {
} | ({
anyOf: {

@@ -20,7 +25,25 @@ $ref: string;

} | {
$ref?: undefined;
$ref: string;
})[] | {
allOf: ({
$ref: string;
properties?: undefined;
type?: undefined;
} | {
properties: {
marks: {
maxItems: number;
type: string;
};
};
type: string;
$ref?: undefined;
})[];
anyOf?: undefined;
} | {
$ref: string;
anyOf: JSONSchema4[];
allOf?: undefined;
};
};
};
export declare function isADFNode(value: ADFNode<any> | ADFNodeGroup): value is ADFNode;
import { ADFAttributes } from '../../types/ADFAttribute';
import { ContentVisitorReturnType } from './adfToJsonVisitor';
export declare function buildRequired(attrs: ADFAttributes, hasContent: boolean, name: string): string[];
export declare function buildVariantRequired(content: ContentVisitorReturnType[]): {
required: string[];
} | {
required?: undefined;
};

@@ -109,2 +109,9 @@ import { ADFNode } from '../adfNode';

/**
* This is specifically for the JSON Schema
*
* If true, it means marks are not allowed on this node.
* This is different to simply having an empty mark list.
*/
noMarks?: boolean;
/**
* https://prosemirror.net/docs/ref/#model.NodeSpec.selectable

@@ -111,0 +118,0 @@ *

{
"name": "@atlaskit/adf-schema-generator",
"version": "1.17.4",
"version": "1.17.5",
"description": "Generates ADF and PM schemas",

@@ -28,4 +28,5 @@ "repository": "https://bitbucket.org/atlassian/adf-schema",

"devDependencies": {
"@types/lodash": "^4.17.4"
"@types/lodash": "^4.17.4",
"@types/json-schema": "^7.0.15"
}
}

@@ -49,2 +49,10 @@ import type { TransformerNames } from './transforms/transformerNames';

/**
* If true, the node doesn't allow marks to be set.
* This is stricter than simply having an empty marks list.
*/
hasNoMarks() {
return this.#spec.noMarks;
}
/**
* Define a node.

@@ -62,2 +70,7 @@ *

this.#spec = spec;
if (spec.noMarks && spec.marks?.length > 0) {
throw new Error('Node with noMarks true has marks');
}
this.#marks = spec.marks?.map((mark) => mark.getType()) ?? [];

@@ -64,0 +77,0 @@ return this;

@@ -11,2 +11,3 @@ import type { ADFNode } from '../../adfNode';

} from './adfToJsonVisitor';
import type { JSONSchema4 } from 'json-schema';

@@ -27,3 +28,3 @@ function transform(adf: ADFNode<[string], ADFNodeSpec>) {

result: Record<string, NodeVisitorReturnType>,
): Record<string, any> {
): JSONSchema4 {
const formattedResult = {};

@@ -35,3 +36,3 @@

const finalResult: Record<string, any> = {
const finalResult: JSONSchema4 = {
$ref: '#/definitions/doc_node',

@@ -47,4 +48,4 @@ description: 'Schema for Atlassian Document Format.',

// Placeholder
export function adfToJSON(adf: ADFNode<[string], any>): Record<string, any> {
export function adfToJSON(adf: ADFNode<[string], any>): JSONSchema4 {
return transform(adf);
}

@@ -0,1 +1,7 @@

import uniqBy from 'lodash/uniqBy';
import { JSONSchema4 } from 'json-schema-types';
import { ADFNode } from '../../adfNode';
import { ADFNodeGroup } from '../../types/ADFNodeGroup';
import { ADFNodeContentSpec } from '../../types/ADFNodeSpec';
import { JSONSchemaTransformerName } from '../transformerNames';
import { ContentVisitorReturnType } from './adfToJsonVisitor';

@@ -7,2 +13,3 @@ import { resolveName } from './inconsistentNameResolver';

name: string,
adfNodeContent: Array<ADFNodeContentSpec>,
) {

@@ -12,53 +19,94 @@ if (content.length === 0) return {};

const minItems = determineMinItems(content, name);
const items = determineItems(content, name);
const items = determineItems(content, name, adfNodeContent);
const maxItems = determineMaxItems(content, adfNodeContent);
return { content: { type: 'array', items, ...minItems } };
return {
content: {
type: 'array',
items,
...(minItems !== null ? { minItems } : {}),
...(maxItems !== null ? { maxItems } : {}),
},
};
}
function isADFGroup(value: ADFNode<any> | ADFNodeGroup): value is ADFNodeGroup {
return value && 'group' in value;
}
export function isADFNode(
value: ADFNode<any> | ADFNodeGroup,
): value is ADFNode {
return value && value instanceof ADFNode;
}
function determineItems(
content: Array<ContentVisitorReturnType>,
name: string,
adfNodeContent: Array<ADFNodeContentSpec>,
) {
// These are arrays instead of objects in the JSON Schema
if (name === 'listItem' || name === 'taskList') {
return handleListContent(content);
}
// TODO: codeblock is insane
// TODO: codeblock has a variant pattern instead of a normal node pattern
// This is probably an expanded reference to the inline code node, a variant of text node
// To remove this exception, we must update the JSON Schema to use a reference here instead
if (name === 'codeBlock') {
return {};
return {
allOf: [
{
$ref: '#/definitions/text_node',
},
{
properties: {
marks: {
maxItems: 0,
type: 'array',
},
},
type: 'object',
},
],
};
}
// If there is one piece of content, defined as $zeroPlus($or([content])) or $onePlus($or([content])), then it's a single $ref in an object
if (content.length === 1 && content[0].contentTypes.length === 1) {
if (name === 'doc') {
const jsonSchema: JSONSchema4[] = adfNodeContent.reduce((acc, value) => {
// We're expecting a single one+ with a nested $or
if (value.type === '$one+' && value.content.type === '$or') {
return [
...acc,
...flattenContent(value.content.content).map((node) => ({
$ref: `#/definitions/${resolveName(node.getName())}`,
})),
];
}
return acc;
}, [] as JSONSchema4[]);
return {
$ref: `#/definitions/${resolveName(content[0].contentTypes[0])}`,
anyOf: uniqBy(jsonSchema, (v) => v.$ref),
};
}
if (content.length === 1 && content[0].contentTypes.length > 1) {
return handleObjectContent(content[0]);
}
return {};
const processedContentGroups = processContentGroups(content);
// The JSON schema omits the array if there is only 1 item
return flattenArray(processedContentGroups);
}
function handleObjectContent(content: ContentVisitorReturnType) {
const itemsArray = content.contentTypes.map((piece) => {
return { $ref: `#/definitions/${resolveName(piece)}` };
function processContentTypes(contentTypes: string[]) {
const itemsArray = [];
contentTypes.forEach((piece) => {
itemsArray.push({ $ref: `#/definitions/${resolveName(piece)}` });
});
return { anyOf: itemsArray };
// We flatten an array here as well, but using anyOf to fit the JSON schema
if (itemsArray.length === 1) {
return itemsArray[0];
} else {
return { anyOf: itemsArray };
}
}
function handleListContent(content: Array<ContentVisitorReturnType>) {
function processContentGroups(content: Array<ContentVisitorReturnType>) {
const contentArray: Array<{ anyOf: { $ref: string }[] } | { $ref: string }> =
[];
content.forEach((item) => {
if (item.contentTypes.length === 1) {
contentArray.push({
$ref: `#/definitions/${resolveName(item.contentTypes[0])}`,
});
} else if (item.contentTypes.length > 1) {
contentArray.push(handleObjectContent(item));
}
contentArray.push(processContentTypes(item.contentTypes));
});

@@ -72,16 +120,80 @@ return contentArray;

) {
let minItems: { minItems: 1 | 0 };
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one minItem value for all content on a node.
if (!content) return null;
const { minItems } = content.find((v) => !isNaN(v.minItems)) ?? {};
const { range } = content.find((v) => v.range) ?? {};
content.forEach((value) => {
// Despite being oneplus, tableRow and doc have no minItems field
if (value.minItems === 1 && name !== 'tableRow' && name !== 'doc') {
minItems = { minItems: 1 };
// Despite being oneplus, tableRow and doc have no minItems field
if (
minItems === 1 &&
name !== 'tableRow' &&
name !== 'doc' &&
name !== 'layoutSection'
) {
return minItems;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
if (minItems === 0 && name === 'caption') {
return minItems;
}
// If it's a range, take minItems from that
if (range) {
return range.min;
}
return null;
}
// This function is not comprehensive, it is only defined for certain inputs
function determineMaxItems(
content: Array<ContentVisitorReturnType>,
adfNodeContent: Array<ADFNodeContentSpec>,
) {
// Despite it being possible for there to be multiple content groups on one node in DSL,
// the JSON schema has only one maxItem value for all content on a node.
// If there's only one item, we can simply calculate it and return
if (content.length === 1) {
// If it's a range, grab maxItems from that
if (content[0].range) {
return content[0].range.max;
}
// Only caption has minItems 0. If we can confirm it's redundant, we can remove this
else if (name === 'caption' && value.minItems === 0) {
minItems = { minItems: 0 };
} else if (adfNodeContent.length > 1) {
const types = adfNodeContent.map((v) => v.type);
if (types.includes('$one+') || types.includes('$zero+')) {
return null;
}
});
return minItems;
const maxItems = types.filter((v) => v === '$or').length;
return maxItems || null;
}
return null;
}
function flattenArray<T>(array: Array<T>) {
if (array.length === 1) {
return array[0];
} else {
return array;
}
}
/**
* Flattens ADF groups and nodes into an array of nodes.
* @param content
* @returns ADFNode[]
*/
function flattenContent(content: (ADFNodeGroup | ADFNode)[]): ADFNode[] {
return content.reduce((acc, item) => {
if (isADFGroup(item)) {
// Expand the group into its member nodes
return [...acc, ...flattenContent(item.members)];
} else if (isADFNode(item) && !item.isIgnored(JSONSchemaTransformerName)) {
return [...acc, item];
}
return acc;
}, [] as ADFNode[]);
}

@@ -7,3 +7,4 @@ import { ADFNode } from '../../adfNode';

import { buildContent } from './contentBuilder';
import { buildRequired } from './requiredBuilder';
import { resolveName } from './inconsistentNameResolver';
import { buildRequired, buildVariantRequired } from './requiredBuilder';

@@ -26,3 +27,3 @@ export const buildNode = (

.filter(Boolean) || [];
const marks = buildNodeMarks(nodeMarks);
const marks = buildNodeMarks(nodeMarks, node.hasNoMarks());
const attrs = buildAttrs(node.getSpec().attrs);

@@ -32,3 +33,7 @@ // Strangely, text has this extra property that looks like an attribute

node.getName() === 'text' ? { text: { minLength: 1, type: 'string' } } : {};
const jsonContent = buildContent(content, node.getName());
const jsonContent = buildContent(
content,
node.getName(),
node.getSpec().content,
);
const version = node.getSpec().version;

@@ -57,2 +62,26 @@ const jsonVersion = version ? { version: { enum: [version] } } : {};

json.required = required;
} else {
const required = buildVariantRequired(content);
const allOfItem: Record<string, any> = {
type: 'object',
properties: {
...marks,
...jsonContent,
},
...required,
};
if (node.getName() === 'layoutSection_full') {
// Only on the layoutSection_full variant we specify type
allOfItem.properties.type = { enum: ['layoutSection'] };
allOfItem.required = ['type', 'content'];
allOfItem.additionalProperties = false;
}
json.allOf = [
{
$ref: `#/definitions/${resolveName(node.getBase().getName())}`,
},
allOfItem,
];
}

@@ -63,3 +92,11 @@

function buildNodeMarks(nodeMarks: string[]) {
function buildNodeMarks(nodeMarks: string[], hasNoMarks: boolean) {
if (hasNoMarks) {
return {
marks: {
type: 'array',
maxItems: 0,
},
};
}
if (nodeMarks.length === 0) return {};

@@ -66,0 +103,0 @@ if (nodeMarks.length === 1)

import { ADFAttributes } from '../../types/ADFAttribute';
import { isAnyOf } from '../../utils/isAnyOf';
import { ContentVisitorReturnType } from './adfToJsonVisitor';

@@ -64,1 +65,8 @@ export function buildRequired(

}
export function buildVariantRequired(content: ContentVisitorReturnType[]) {
if (content[0]?.minItems >= 1 || Boolean(content[0]?.range?.type)) {
return { required: ['content'] };
}
return {};
}

@@ -42,3 +42,3 @@ import type { ADFNode } from '../../adfNode';

if (node.isIgnored(PMSpecTransformerName)) {
return;
return { node };
}

@@ -64,4 +64,11 @@ const marks = node.getSpec().marks ?? [];

group: (group, members) => {
nodeGroupMap[group.group] = group.members.map((m) => m.getName());
return { group: group.group, members };
nodeGroupMap[group.group] = group.members
.filter((node) => !node.isIgnored(PMSpecTransformerName))
.map((node) => node.getName());
return {
group: group.group,
members: members.filter(
(m) => !m.node.isIgnored(PMSpecTransformerName),
),
};
},

@@ -68,0 +75,0 @@ $or(children) {

@@ -128,2 +128,10 @@ import { ADFNode } from '../adfNode';

/**
* This is specifically for the JSON Schema
*
* If true, it means marks are not allowed on this node.
* This is different to simply having an empty mark list.
*/
noMarks?: boolean;
/**
* https://prosemirror.net/docs/ref/#model.NodeSpec.selectable

@@ -130,0 +138,0 @@ *

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc