@sanity/validation 2.21.12-shopify.7 → 2.22.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.
- package/dist/dts/Rule.d.ts.map +1 -1
- package/dist/dts/util/normalizeValidationRules.d.ts.map +1 -1
- package/dist/dts/validateDocument.d.ts.map +1 -1
- package/dist/dts/validators/objectValidator.d.ts.map +1 -1
- package/lib/Rule.js +11 -7
- package/lib/util/normalizeValidationRules.js +0 -3
- package/lib/validateDocument.js +24 -1
- package/lib/validators/objectValidator.js +0 -21
- package/package.json +5 -5
- package/src/Rule.ts +8 -5
- package/src/util/normalizeValidationRules.ts +0 -2
- package/src/validateDocument.test.ts +230 -2
- package/src/validateDocument.ts +34 -1
- package/src/validators/objectValidator.ts +0 -9
- package/dist/dts/validators/blockValidator.d.ts +0 -3
- package/dist/dts/validators/blockValidator.d.ts.map +0 -1
- package/lib/validators/blockValidator.js +0 -65
- package/src/validators/blockValidator.ts +0 -54
package/dist/dts/Rule.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Rule.d.ts","sourceRoot":"","sources":["../../src/Rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,SAAS,EAUV,MAAM,eAAe,CAAA;AA0DtB,QAAA,MAAM,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"Rule.d.ts","sourceRoot":"","sources":["../../src/Rule.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,SAAS,EAUV,MAAM,eAAe,CAAA;AA0DtB,QAAA,MAAM,IAAI,EAAE,SA2VX,CAAA;AAED,eAAe,IAAI,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalizeValidationRules.d.ts","sourceRoot":"","sources":["../../../src/util/normalizeValidationRules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,IAAI,EAAqB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"normalizeValidationRules.d.ts","sourceRoot":"","sources":["../../../src/util/normalizeValidationRules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAE,IAAI,EAAqB,MAAM,eAAe,CAAA;AA6ElE;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,GAAG,IAAI,EAAE,CAqCxF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validateDocument.d.ts","sourceRoot":"","sources":["../../src/validateDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,MAAM,EACN,UAAU,EACV,iBAAiB,EACjB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"validateDocument.d.ts","sourceRoot":"","sources":["../../src/validateDocument.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,MAAM,EACN,UAAU,EACV,iBAAiB,EACjB,gBAAgB,EAMjB,MAAM,eAAe,CAAA;AAYtB;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,UAAU,EAAE,GACvB,UAAU,GAAG,SAAS,CAiBxB;AAED,wBAA8B,gBAAgB,CAC5C,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA0B7B;AAED;;;;;;GAMG;AACH,aAAK,iBAAiB,CAAC,CAAC,IAAI;KACzB,CAAC,IAAI,MAAM,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,SAAS,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS;CAC5F,CAAA;AAED,aAAK,mBAAmB,GAAG;IACzB,KAAK,EAAE,OAAO,CAAA;CACf,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;AAExC,wBAAsB,YAAY,CAAC,EACjC,KAAK,EACL,IAAI,EACJ,IAAS,EACT,MAAM,EACN,GAAG,aAAa,EACjB,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA4HnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/objectValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AAKxC,QAAA,MAAM,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"objectValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/objectValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAA;AAKxC,QAAA,MAAM,gBAAgB,EAAE,UAqCvB,CAAA;AAED,eAAe,gBAAgB,CAAA"}
|
package/lib/Rule.js
CHANGED
|
@@ -232,6 +232,17 @@ var Rule = (_temp = _class = class Rule {
|
|
|
232
232
|
constraint: fn
|
|
233
233
|
}]);
|
|
234
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* @deprecated use `Rule.custom` instead
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
block(fn) {
|
|
241
|
+
return this.cloneWithRules([{
|
|
242
|
+
flag: 'custom',
|
|
243
|
+
constraint: fn
|
|
244
|
+
}]);
|
|
245
|
+
}
|
|
235
246
|
|
|
236
247
|
min(len) {
|
|
237
248
|
return this.cloneWithRules([{
|
|
@@ -383,13 +394,6 @@ var Rule = (_temp = _class = class Rule {
|
|
|
383
394
|
}]);
|
|
384
395
|
}
|
|
385
396
|
|
|
386
|
-
block(blockValidator) {
|
|
387
|
-
return this.cloneWithRules([{
|
|
388
|
-
flag: 'block',
|
|
389
|
-
constraint: blockValidator
|
|
390
|
-
}]);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
397
|
fields(rules) {
|
|
394
398
|
if (this._type !== 'Object') {
|
|
395
399
|
throw new Error('fields() can only be called on an object type');
|
|
@@ -7,8 +7,6 @@ exports.default = normalizeValidationRules;
|
|
|
7
7
|
|
|
8
8
|
var _Rule = _interopRequireDefault(require("../Rule"));
|
|
9
9
|
|
|
10
|
-
var _blockValidator = require("../validators/blockValidator");
|
|
11
|
-
|
|
12
10
|
var _slugValidator = require("../validators/slugValidator");
|
|
13
11
|
|
|
14
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -61,7 +59,6 @@ function baseRuleReducer(inputRule, type) {
|
|
|
61
59
|
if (type.name === 'slug') return baseRule.custom(_slugValidator.slugValidator);
|
|
62
60
|
if (type.name === 'reference') return baseRule.reference();
|
|
63
61
|
if (type.name === 'email') return baseRule.email();
|
|
64
|
-
if (type.name === 'block') return baseRule.block(_blockValidator.blockValidator);
|
|
65
62
|
return baseRule;
|
|
66
63
|
}
|
|
67
64
|
|
package/lib/validateDocument.js
CHANGED
|
@@ -192,9 +192,32 @@ function _validateItem() {
|
|
|
192
192
|
} : value.indexOf(item)),
|
|
193
193
|
type: resolveTypeForArrayItem(item, type.of)
|
|
194
194
|
}))));
|
|
195
|
+
} // markDefs also do no run nested validation if the parent object is undefined
|
|
196
|
+
// for a similar reason to arrays
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
var shouldRunNestedValidationForMarkDefs = (0, _types.isBlock)(value) && value.markDefs.length && (0, _types.isBlockSchemaType)(type);
|
|
200
|
+
|
|
201
|
+
if (shouldRunNestedValidationForMarkDefs) {
|
|
202
|
+
var _type$fields = _slicedToArray(type.fields, 1),
|
|
203
|
+
spanChildrenField = _type$fields[0];
|
|
204
|
+
|
|
205
|
+
var spanType = spanChildrenField.type.of.find(_types.isSpanSchemaType);
|
|
206
|
+
var annotations = ((spanType === null || spanType === void 0 ? void 0 : spanType.annotations) || []).reduce((map, annotationType) => {
|
|
207
|
+
map.set(annotationType.name, annotationType);
|
|
208
|
+
return map;
|
|
209
|
+
}, new Map());
|
|
210
|
+
nestedChecks = nestedChecks.concat(value.markDefs.map(markDef => validateItem(_objectSpread(_objectSpread({}, restOfContext), {}, {
|
|
211
|
+
parent: value,
|
|
212
|
+
value: markDef,
|
|
213
|
+
path: path.concat(['markDefs', {
|
|
214
|
+
_key: markDef._key
|
|
215
|
+
}]),
|
|
216
|
+
type: annotations.get(markDef._type)
|
|
217
|
+
}))));
|
|
195
218
|
}
|
|
196
219
|
|
|
197
|
-
var results = (yield Promise.all([...selfChecks, ...nestedChecks])).flat(); // run `
|
|
220
|
+
var results = (yield Promise.all([...selfChecks, ...nestedChecks])).flat(); // run `uniqBy` if `_fieldRules` are present because they can
|
|
198
221
|
// cause repeat markers
|
|
199
222
|
|
|
200
223
|
if (rules.some(rule => rule._fieldRules)) {
|
|
@@ -9,10 +9,6 @@ var _genericValidator = _interopRequireDefault(require("./genericValidator"));
|
|
|
9
9
|
|
|
10
10
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
11
11
|
|
|
12
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
13
|
-
|
|
14
|
-
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
15
|
-
|
|
16
12
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
17
13
|
|
|
18
14
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
@@ -46,23 +42,6 @@ var objectValidators = _objectSpread(_objectSpread({}, _genericValidator.default
|
|
|
46
42
|
|
|
47
43
|
return true;
|
|
48
44
|
},
|
|
49
|
-
block: function () {
|
|
50
|
-
var _block = _asyncToGenerator(function* (validateBlock, value, message, context) {
|
|
51
|
-
var result = yield validateBlock(value, context);
|
|
52
|
-
|
|
53
|
-
if (typeof result === 'string') {
|
|
54
|
-
return message || result;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return result;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
function block(_x, _x2, _x3, _x4) {
|
|
61
|
-
return _block.apply(this, arguments);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return block;
|
|
65
|
-
}(),
|
|
66
45
|
assetRequired: (flag, value, message) => {
|
|
67
46
|
if (!value || !value.asset || !value.asset._ref) {
|
|
68
47
|
var assetType = flag.assetType || 'Asset';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/validation",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.0",
|
|
4
4
|
"description": "Validation and warning infrastructure for Sanity projects",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"types": "./dist/dts",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"directory": "packages/@sanity/validation"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@sanity/client": "2.21.
|
|
36
|
-
"@sanity/schema": "2.
|
|
35
|
+
"@sanity/client": "2.21.10",
|
|
36
|
+
"@sanity/schema": "2.22.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@sanity/client": "^2.0.0"
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@sanity/types": "2.
|
|
47
|
+
"@sanity/types": "2.22.0",
|
|
48
48
|
"date-fns": "^2.16.1",
|
|
49
49
|
"lodash": "^4.17.15"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "f41f6620cf21f38352e1587ea7a1c350b33d2c33"
|
|
52
52
|
}
|
package/src/Rule.ts
CHANGED
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
RuleClass,
|
|
5
5
|
CustomValidator,
|
|
6
6
|
RuleSpecConstraint,
|
|
7
|
-
BlockValidator,
|
|
8
7
|
FieldRules,
|
|
9
8
|
ValidationContext,
|
|
10
9
|
RuleSpec,
|
|
11
10
|
ValidationMarker,
|
|
12
11
|
RuleTypeConstraint,
|
|
13
12
|
Validator,
|
|
13
|
+
Block,
|
|
14
14
|
} from '@sanity/types'
|
|
15
15
|
import {cloneDeep, get} from 'lodash'
|
|
16
16
|
import ValidationErrorClass from './ValidationError'
|
|
@@ -219,6 +219,13 @@ const Rule: RuleClass = class Rule implements IRule {
|
|
|
219
219
|
return this.cloneWithRules([{flag: 'custom', constraint: fn as CustomValidator}])
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
/**
|
|
223
|
+
* @deprecated use `Rule.custom` instead
|
|
224
|
+
*/
|
|
225
|
+
block(fn: CustomValidator<Block>): Rule {
|
|
226
|
+
return this.cloneWithRules([{flag: 'custom', constraint: fn as CustomValidator}])
|
|
227
|
+
}
|
|
228
|
+
|
|
222
229
|
min(len: number): Rule {
|
|
223
230
|
return this.cloneWithRules([{flag: 'min', constraint: len}])
|
|
224
231
|
}
|
|
@@ -336,10 +343,6 @@ const Rule: RuleClass = class Rule implements IRule {
|
|
|
336
343
|
return this.cloneWithRules([{flag: 'reference'}])
|
|
337
344
|
}
|
|
338
345
|
|
|
339
|
-
block(blockValidator: BlockValidator): Rule {
|
|
340
|
-
return this.cloneWithRules([{flag: 'block', constraint: blockValidator}])
|
|
341
|
-
}
|
|
342
|
-
|
|
343
346
|
fields(rules: FieldRules): Rule {
|
|
344
347
|
if (this._type !== 'Object') {
|
|
345
348
|
throw new Error('fields() can only be called on an object type')
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {SchemaType, Rule, RuleTypeConstraint} from '@sanity/types'
|
|
2
2
|
import RuleClass from '../Rule'
|
|
3
|
-
import {blockValidator} from '../validators/blockValidator'
|
|
4
3
|
import {slugValidator} from '../validators/slugValidator'
|
|
5
4
|
|
|
6
5
|
const ruleConstraintTypes: {[P in Lowercase<RuleTypeConstraint>]: true} = {
|
|
@@ -54,7 +53,6 @@ function baseRuleReducer(inputRule: Rule, type: SchemaType) {
|
|
|
54
53
|
if (type.name === 'slug') return baseRule.custom(slugValidator)
|
|
55
54
|
if (type.name === 'reference') return baseRule.reference()
|
|
56
55
|
if (type.name === 'email') return baseRule.email()
|
|
57
|
-
if (type.name === 'block') return baseRule.block(blockValidator)
|
|
58
56
|
return baseRule
|
|
59
57
|
}
|
|
60
58
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="@sanity/types/parts" />
|
|
2
2
|
|
|
3
|
-
import {Rule, SchemaType, SanityDocument, Schema, ArraySchemaType} from '@sanity/types'
|
|
3
|
+
import {Rule, SchemaType, SanityDocument, Schema, ArraySchemaType, Block} from '@sanity/types'
|
|
4
4
|
import createSchema from 'part:@sanity/base/schema-creator'
|
|
5
5
|
import validateDocument, {resolveTypeForArrayItem, validateItem} from './validateDocument'
|
|
6
6
|
import convertToValidationMarker from './util/convertToValidationMarker'
|
|
@@ -149,14 +149,17 @@ describe('validateItem', () => {
|
|
|
149
149
|
},
|
|
150
150
|
],
|
|
151
151
|
},
|
|
152
|
-
{name: 'registeredString', type: 'string', validation},
|
|
152
|
+
{name: 'registeredString', title: 'Registered String', type: 'string', validation},
|
|
153
153
|
{
|
|
154
|
+
title: 'Registered Object Field',
|
|
154
155
|
name: 'registeredObjectField',
|
|
155
156
|
type: 'object',
|
|
156
157
|
fields: [{name: 'foo', type: 'string', validation}],
|
|
157
158
|
},
|
|
158
159
|
],
|
|
159
160
|
})
|
|
161
|
+
// ensures there are no schema formatting issues
|
|
162
|
+
expect(schema._validation).toHaveLength(0)
|
|
160
163
|
|
|
161
164
|
await expect(
|
|
162
165
|
validateItem({
|
|
@@ -228,6 +231,9 @@ describe('validateItem', () => {
|
|
|
228
231
|
],
|
|
229
232
|
})
|
|
230
233
|
|
|
234
|
+
// ensures there are no schema formatting issues
|
|
235
|
+
expect(schema._validation).toHaveLength(0)
|
|
236
|
+
|
|
231
237
|
await expect(
|
|
232
238
|
validateItem({
|
|
233
239
|
document: undefined,
|
|
@@ -252,17 +258,231 @@ describe('validateItem', () => {
|
|
|
252
258
|
])
|
|
253
259
|
})
|
|
254
260
|
|
|
261
|
+
it('runs nested validation for markDefs', async () => {
|
|
262
|
+
const linkValidationSpy = jest.fn(() => true as const)
|
|
263
|
+
const internalLinkSpy = jest.fn(() => 'mock invalid response')
|
|
264
|
+
|
|
265
|
+
const schema = createSchema({
|
|
266
|
+
types: [
|
|
267
|
+
{
|
|
268
|
+
name: 'post',
|
|
269
|
+
title: 'Post',
|
|
270
|
+
type: 'document',
|
|
271
|
+
fields: [
|
|
272
|
+
{name: 'title', type: 'string'},
|
|
273
|
+
{name: 'body', type: 'string'},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: 'registeredEditor',
|
|
278
|
+
type: 'object',
|
|
279
|
+
title: 'Registered Editor',
|
|
280
|
+
fields: [
|
|
281
|
+
{
|
|
282
|
+
name: 'editor',
|
|
283
|
+
type: 'array',
|
|
284
|
+
of: [
|
|
285
|
+
{
|
|
286
|
+
type: 'block',
|
|
287
|
+
marks: {
|
|
288
|
+
annotations: [
|
|
289
|
+
{
|
|
290
|
+
name: 'exampleAnnotation',
|
|
291
|
+
type: 'object',
|
|
292
|
+
fields: [{name: 'value', type: 'string'}],
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: 'blockTest',
|
|
303
|
+
type: 'document',
|
|
304
|
+
title: 'blockTest',
|
|
305
|
+
fields: [
|
|
306
|
+
{
|
|
307
|
+
name: 'content',
|
|
308
|
+
title: 'Content',
|
|
309
|
+
type: 'array',
|
|
310
|
+
of: [
|
|
311
|
+
{
|
|
312
|
+
type: 'block',
|
|
313
|
+
marks: {
|
|
314
|
+
annotations: [
|
|
315
|
+
{
|
|
316
|
+
name: 'link',
|
|
317
|
+
type: 'object',
|
|
318
|
+
title: 'link',
|
|
319
|
+
fields: [{name: 'url', type: 'url'}],
|
|
320
|
+
validation: (rule: Rule) => rule.custom(linkValidationSpy),
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'internalLink',
|
|
324
|
+
type: 'object',
|
|
325
|
+
title: 'Internal link',
|
|
326
|
+
fields: [{name: 'reference', type: 'reference', to: [{type: 'post'}]}],
|
|
327
|
+
validation: (rule: Rule) => rule.custom(internalLinkSpy),
|
|
328
|
+
},
|
|
329
|
+
{name: 'nestedEditor', type: 'registeredEditor'},
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
expect(schema._validation).toHaveLength(0)
|
|
341
|
+
|
|
342
|
+
const nestedBlock: Block = {
|
|
343
|
+
_type: 'block',
|
|
344
|
+
_key: 'nested-block-key',
|
|
345
|
+
children: [
|
|
346
|
+
{
|
|
347
|
+
_key: 'some-key',
|
|
348
|
+
_type: 'span',
|
|
349
|
+
text: 'hey',
|
|
350
|
+
marks: ['example-annotation-key'],
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
markDefs: [
|
|
354
|
+
{
|
|
355
|
+
_type: 'exampleAnnotation',
|
|
356
|
+
_key: 'example-annotation-key',
|
|
357
|
+
value: 5,
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
style: 'normal',
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const block: Block = {
|
|
364
|
+
_key: 'block-key',
|
|
365
|
+
_type: 'block',
|
|
366
|
+
children: [
|
|
367
|
+
{
|
|
368
|
+
_key: 'child-0',
|
|
369
|
+
_type: 'span',
|
|
370
|
+
marks: ['0', '1', '2'],
|
|
371
|
+
text: 'hey',
|
|
372
|
+
},
|
|
373
|
+
],
|
|
374
|
+
markDefs: [
|
|
375
|
+
{
|
|
376
|
+
_key: '0',
|
|
377
|
+
_type: 'link',
|
|
378
|
+
url: 'https://example.com',
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
_key: '1',
|
|
382
|
+
_type: 'internalLink',
|
|
383
|
+
_ref: 'post-id',
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
_key: '2',
|
|
387
|
+
_type: 'nestedEditor',
|
|
388
|
+
editor: [nestedBlock],
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
style: 'normal',
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const document: SanityDocument = {
|
|
395
|
+
_id: 'mock-id',
|
|
396
|
+
_type: 'blockTest',
|
|
397
|
+
_createdAt: '2021-11-15T21:06:41.812Z',
|
|
398
|
+
_rev: 'example-ref',
|
|
399
|
+
_updatedAt: '2021-11-15T21:06:41.812Z',
|
|
400
|
+
content: [block],
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const result = await validateItem({
|
|
404
|
+
document: document,
|
|
405
|
+
parent: undefined,
|
|
406
|
+
path: [],
|
|
407
|
+
type: schema.get('blockTest'),
|
|
408
|
+
value: document,
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
expect(result).toMatchObject([
|
|
412
|
+
{
|
|
413
|
+
item: {message: 'mock invalid response', paths: []},
|
|
414
|
+
level: 'error',
|
|
415
|
+
path: ['content', {_key: 'block-key'}, 'markDefs', {_key: '1'}],
|
|
416
|
+
type: 'validation',
|
|
417
|
+
},
|
|
418
|
+
// this tests for nested markDef validation
|
|
419
|
+
{
|
|
420
|
+
item: {
|
|
421
|
+
message: 'Expected type "String", got "Number"',
|
|
422
|
+
paths: [],
|
|
423
|
+
},
|
|
424
|
+
level: 'error',
|
|
425
|
+
path: [
|
|
426
|
+
'content',
|
|
427
|
+
{_key: 'block-key'},
|
|
428
|
+
'markDefs',
|
|
429
|
+
{_key: '2'},
|
|
430
|
+
'editor',
|
|
431
|
+
{_key: 'nested-block-key'},
|
|
432
|
+
'markDefs',
|
|
433
|
+
{_key: 'example-annotation-key'},
|
|
434
|
+
'value',
|
|
435
|
+
],
|
|
436
|
+
type: 'validation',
|
|
437
|
+
},
|
|
438
|
+
])
|
|
439
|
+
|
|
440
|
+
expect(linkValidationSpy.mock.calls).toMatchObject([
|
|
441
|
+
[
|
|
442
|
+
{
|
|
443
|
+
_key: '0',
|
|
444
|
+
_type: 'link',
|
|
445
|
+
url: 'https://example.com',
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
document: {_id: 'mock-id'},
|
|
449
|
+
parent: {_key: 'block-key'},
|
|
450
|
+
path: ['content', {_key: 'block-key'}, 'markDefs', {_key: '0'}],
|
|
451
|
+
type: {name: 'link'},
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
])
|
|
455
|
+
|
|
456
|
+
expect(internalLinkSpy.mock.calls).toMatchObject([
|
|
457
|
+
[
|
|
458
|
+
{
|
|
459
|
+
_key: '1',
|
|
460
|
+
_ref: 'post-id',
|
|
461
|
+
_type: 'internalLink',
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
document: {_id: 'mock-id'},
|
|
465
|
+
parent: {_key: 'block-key'},
|
|
466
|
+
path: ['content', {_key: 'block-key'}, 'markDefs', {_key: '1'}],
|
|
467
|
+
type: {name: 'internalLink'},
|
|
468
|
+
},
|
|
469
|
+
],
|
|
470
|
+
])
|
|
471
|
+
})
|
|
472
|
+
|
|
255
473
|
it('resolves an array item type if there is just one type', async () => {
|
|
256
474
|
const schema = createSchema({
|
|
257
475
|
types: [
|
|
258
476
|
{
|
|
259
477
|
name: 'values',
|
|
478
|
+
title: 'Values',
|
|
260
479
|
type: 'array',
|
|
261
480
|
// note that there is only one type available
|
|
262
481
|
of: [{type: 'arrayItem'}],
|
|
263
482
|
validation: (rule: Rule) => rule.required(),
|
|
264
483
|
},
|
|
265
484
|
{
|
|
485
|
+
title: 'Array Item',
|
|
266
486
|
name: 'arrayItem',
|
|
267
487
|
type: 'object',
|
|
268
488
|
fields: [{name: 'title', type: 'string'}],
|
|
@@ -270,6 +490,9 @@ describe('validateItem', () => {
|
|
|
270
490
|
],
|
|
271
491
|
})
|
|
272
492
|
|
|
493
|
+
// ensures there are no schema formatting issues
|
|
494
|
+
expect(schema._validation).toHaveLength(0)
|
|
495
|
+
|
|
273
496
|
const values = [
|
|
274
497
|
{
|
|
275
498
|
// note how this doesn't have a _type
|
|
@@ -305,6 +528,7 @@ describe('validateItem', () => {
|
|
|
305
528
|
{
|
|
306
529
|
name: 'root',
|
|
307
530
|
type: 'object',
|
|
531
|
+
title: 'Root',
|
|
308
532
|
fields: [
|
|
309
533
|
{
|
|
310
534
|
name: 'level1Object',
|
|
@@ -341,6 +565,10 @@ describe('validateItem', () => {
|
|
|
341
565
|
],
|
|
342
566
|
})
|
|
343
567
|
|
|
568
|
+
// ensures there are no schema formatting issues
|
|
569
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
570
|
+
expect((schema as any)._validation).toHaveLength(0)
|
|
571
|
+
|
|
344
572
|
const value = {
|
|
345
573
|
level1Object: {level3String: 'a string'},
|
|
346
574
|
level1Array: [{level2Number: 5}],
|
package/src/validateDocument.ts
CHANGED
|
@@ -6,6 +6,9 @@ import {
|
|
|
6
6
|
ValidationMarker,
|
|
7
7
|
isKeyedObject,
|
|
8
8
|
isTypedObject,
|
|
9
|
+
isBlock,
|
|
10
|
+
isBlockSchemaType,
|
|
11
|
+
isSpanSchemaType,
|
|
9
12
|
} from '@sanity/types'
|
|
10
13
|
import {uniqBy} from 'lodash'
|
|
11
14
|
import typeString from './util/typeString'
|
|
@@ -180,9 +183,39 @@ export async function validateItem({
|
|
|
180
183
|
)
|
|
181
184
|
}
|
|
182
185
|
|
|
186
|
+
// markDefs also do no run nested validation if the parent object is undefined
|
|
187
|
+
// for a similar reason to arrays
|
|
188
|
+
const shouldRunNestedValidationForMarkDefs =
|
|
189
|
+
isBlock(value) && value.markDefs.length && isBlockSchemaType(type)
|
|
190
|
+
|
|
191
|
+
if (shouldRunNestedValidationForMarkDefs) {
|
|
192
|
+
const [spanChildrenField] = type.fields
|
|
193
|
+
const spanType = spanChildrenField.type.of.find(isSpanSchemaType)
|
|
194
|
+
|
|
195
|
+
const annotations = (spanType?.annotations || []).reduce<Map<string, SchemaType>>(
|
|
196
|
+
(map, annotationType) => {
|
|
197
|
+
map.set(annotationType.name, annotationType)
|
|
198
|
+
return map
|
|
199
|
+
},
|
|
200
|
+
new Map()
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
nestedChecks = nestedChecks.concat(
|
|
204
|
+
value.markDefs.map((markDef) =>
|
|
205
|
+
validateItem({
|
|
206
|
+
...restOfContext,
|
|
207
|
+
parent: value,
|
|
208
|
+
value: markDef,
|
|
209
|
+
path: path.concat(['markDefs', {_key: markDef._key}]),
|
|
210
|
+
type: annotations.get(markDef._type),
|
|
211
|
+
})
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
183
216
|
const results = (await Promise.all([...selfChecks, ...nestedChecks])).flat()
|
|
184
217
|
|
|
185
|
-
// run `
|
|
218
|
+
// run `uniqBy` if `_fieldRules` are present because they can
|
|
186
219
|
// cause repeat markers
|
|
187
220
|
if (rules.some((rule) => rule._fieldRules)) {
|
|
188
221
|
return uniqBy(results, (rule) => JSON.stringify(rule))
|
|
@@ -32,15 +32,6 @@ const objectValidators: Validators = {
|
|
|
32
32
|
return true
|
|
33
33
|
},
|
|
34
34
|
|
|
35
|
-
block: async (validateBlock, value, message, context) => {
|
|
36
|
-
const result = await validateBlock(value, context)
|
|
37
|
-
if (typeof result === 'string') {
|
|
38
|
-
return message || result
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return result
|
|
42
|
-
},
|
|
43
|
-
|
|
44
35
|
assetRequired: (flag, value, message) => {
|
|
45
36
|
if (!value || !value.asset || !value.asset._ref) {
|
|
46
37
|
const assetType = flag.assetType || 'Asset'
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"blockValidator.d.ts","sourceRoot":"","sources":["../../../src/validators/blockValidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,cAAc,EAAC,MAAM,eAAe,CAAA;AAG9D,eAAO,MAAM,cAAc,EAAE,cAkD5B,CAAA"}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.blockValidator = void 0;
|
|
7
|
-
|
|
8
|
-
var _validateDocument = require("../validateDocument");
|
|
9
|
-
|
|
10
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
11
|
-
|
|
12
|
-
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
13
|
-
|
|
14
|
-
var blockValidator = /*#__PURE__*/function () {
|
|
15
|
-
var _ref = _asyncToGenerator(function* (value, context) {
|
|
16
|
-
var _type$fields, _type$fields$find;
|
|
17
|
-
|
|
18
|
-
if (value.markDefs.length === 0) return [];
|
|
19
|
-
var type = context.type;
|
|
20
|
-
|
|
21
|
-
if (!type) {
|
|
22
|
-
throw new Error("Schema `type` was not provided in validation context");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
if (type.jsonType !== 'object') {
|
|
26
|
-
throw new Error("Expected schema type with jsonType `object` but found `".concat(type.jsonType, "` instead."));
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var childrenType = (_type$fields = type.fields) === null || _type$fields === void 0 ? void 0 : (_type$fields$find = _type$fields.find(field => field.name === 'children')) === null || _type$fields$find === void 0 ? void 0 : _type$fields$find.type;
|
|
30
|
-
var spanType = (childrenType === null || childrenType === void 0 ? void 0 : childrenType.jsonType) === 'array' ? childrenType === null || childrenType === void 0 ? void 0 : childrenType.of.find(ofType => ofType.name === 'span') : null; // Validate every markDef (annotation) value
|
|
31
|
-
// eslint-disable-next-line no-warning-comments
|
|
32
|
-
// @ts-expect-error TODO (eventually): fix these types
|
|
33
|
-
|
|
34
|
-
var activeAnnotationTypes = spanType === null || spanType === void 0 ? void 0 : spanType.annotations.filter(annotation => // eslint-disable-next-line no-warning-comments
|
|
35
|
-
// @ts-expect-error TODO (eventually): fix these types
|
|
36
|
-
value.markDefs.map(def => def._type).includes(annotation.name));
|
|
37
|
-
var annotationValidations = [];
|
|
38
|
-
value.markDefs.forEach(markDef => {
|
|
39
|
-
var annotationType = activeAnnotationTypes.find(aType => aType.name === markDef._type);
|
|
40
|
-
var validations = (0, _validateDocument.validateItem)({
|
|
41
|
-
document: context.document,
|
|
42
|
-
value: markDef,
|
|
43
|
-
type: annotationType,
|
|
44
|
-
path: (context.path || []).concat(['markDefs', {
|
|
45
|
-
_key: markDef._key
|
|
46
|
-
}]),
|
|
47
|
-
parent: value
|
|
48
|
-
});
|
|
49
|
-
annotationValidations.push(validations);
|
|
50
|
-
});
|
|
51
|
-
var results = yield Promise.all(annotationValidations);
|
|
52
|
-
var flattened = results.flat();
|
|
53
|
-
if (!flattened.length) return [];
|
|
54
|
-
return flattened.map(res => {
|
|
55
|
-
res.item.paths = [res.path];
|
|
56
|
-
return res.item;
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
return function blockValidator(_x, _x2) {
|
|
61
|
-
return _ref.apply(this, arguments);
|
|
62
|
-
};
|
|
63
|
-
}();
|
|
64
|
-
|
|
65
|
-
exports.blockValidator = blockValidator;
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import {ValidationMarker, BlockValidator} from '@sanity/types'
|
|
2
|
-
import {validateItem} from '../validateDocument'
|
|
3
|
-
|
|
4
|
-
export const blockValidator: BlockValidator = async (value, context) => {
|
|
5
|
-
if (value.markDefs.length === 0) return []
|
|
6
|
-
|
|
7
|
-
const {type} = context
|
|
8
|
-
|
|
9
|
-
if (!type) {
|
|
10
|
-
throw new Error(`Schema \`type\` was not provided in validation context`)
|
|
11
|
-
}
|
|
12
|
-
if (type.jsonType !== 'object') {
|
|
13
|
-
throw new Error(
|
|
14
|
-
`Expected schema type with jsonType \`object\` but found \`${type.jsonType}\` instead.`
|
|
15
|
-
)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const childrenType = type.fields?.find((field) => field.name === 'children')?.type
|
|
19
|
-
const spanType =
|
|
20
|
-
childrenType?.jsonType === 'array'
|
|
21
|
-
? childrenType?.of.find((ofType) => ofType.name === 'span')
|
|
22
|
-
: null
|
|
23
|
-
|
|
24
|
-
// Validate every markDef (annotation) value
|
|
25
|
-
// eslint-disable-next-line no-warning-comments
|
|
26
|
-
// @ts-expect-error TODO (eventually): fix these types
|
|
27
|
-
const activeAnnotationTypes: any[] = spanType?.annotations.filter((annotation) =>
|
|
28
|
-
// eslint-disable-next-line no-warning-comments
|
|
29
|
-
// @ts-expect-error TODO (eventually): fix these types
|
|
30
|
-
value.markDefs.map((def) => def._type).includes(annotation.name)
|
|
31
|
-
)
|
|
32
|
-
const annotationValidations: Promise<ValidationMarker[]>[] = []
|
|
33
|
-
value.markDefs.forEach((markDef: any) => {
|
|
34
|
-
const annotationType = activeAnnotationTypes.find((aType) => aType.name === markDef._type)
|
|
35
|
-
const validations = validateItem({
|
|
36
|
-
document: context.document,
|
|
37
|
-
value: markDef,
|
|
38
|
-
type: annotationType,
|
|
39
|
-
path: (context.path || []).concat(['markDefs', {_key: markDef._key}]),
|
|
40
|
-
parent: value,
|
|
41
|
-
})
|
|
42
|
-
annotationValidations.push(validations)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
const results = await Promise.all(annotationValidations)
|
|
46
|
-
const flattened = results.flat()
|
|
47
|
-
|
|
48
|
-
if (!flattened.length) return []
|
|
49
|
-
|
|
50
|
-
return flattened.map((res) => {
|
|
51
|
-
res.item.paths = [res.path]
|
|
52
|
-
return res.item
|
|
53
|
-
})
|
|
54
|
-
}
|