Skip to content

Commit

Permalink
[schema] Catch and display errors for falsey/incorrectly imported typ…
Browse files Browse the repository at this point in the history
…e defs (#1316)
  • Loading branch information
rexxars committed May 24, 2019
1 parent 07bc78f commit c6cd728
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 8 deletions.
14 changes: 9 additions & 5 deletions packages/@sanity/schema/src/core/traverseSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ export default function traverseSchema(

const reservedTypeNames = FUTURE_RESERVED.concat(coreTypeNames)

const typeNames = types.map(typeDef => typeDef.name).filter(Boolean)
const typeNames = types.map(typeDef => typeDef && typeDef.name).filter(Boolean)

coreTypes.forEach(coreType => {
coreTypesRegistry[coreType.name] = coreType
})

types.forEach((type, i) => {
// Allocate a placeholder for each type
registry[type.name || `__unnamed_${i}`] = {}
registry[(type && type.name) || `__unnamed_${i}`] = {}
})

function getType(typeName) {
Expand All @@ -69,14 +69,15 @@ export default function traverseSchema(
return typeName === 'type' || reservedTypeNames.includes(typeName)
}

const visitType = isRoot => typeDef => {
const visitType = isRoot => (typeDef, index) => {
return visitor(typeDef, {
visit: visitType(false),
isRoot,
getType,
getTypeNames,
isReserved,
isDuplicate
isDuplicate,
index
})
}

Expand All @@ -85,7 +86,10 @@ export default function traverseSchema(
})

types.forEach((typeDef, i) => {
Object.assign(registry[typeDef.name || `__unnamed_${i}`], visitType(true)(typeDef))
Object.assign(
registry[(typeDef && typeDef.name) || `__unnamed_${i}`],
visitType(true)(typeDef, i)
)
})

return {
Expand Down
9 changes: 7 additions & 2 deletions packages/@sanity/schema/src/sanity/validateSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const typeVisitors = {
reference: reference
}

const NOOP_VISITOR = schemaDef => ({
const getNoopVisitor = visitorContext => schemaDef => ({
name: `<unnamed_type_@_index_${visitorContext.index}>`,
...schemaDef,
_problems: []
})
Expand All @@ -45,10 +46,14 @@ function combine(...visitors) {
// Clean up the api
export default function validateSchema(schemaTypes) {
return traverseSchema(schemaTypes, (schemaDef, visitorContext) => {
const typeVisitor = (schemaDef.type && typeVisitors[schemaDef.type]) || NOOP_VISITOR
const typeVisitor =
(schemaDef && schemaDef.type && typeVisitors[schemaDef.type]) ||
getNoopVisitor(visitorContext)

if (visitorContext.isRoot) {
return combine(rootType, common, typeVisitor)(schemaDef, visitorContext)
}

return combine(common, typeVisitor)(schemaDef, visitorContext)
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type {Severity, ValidationResult} from '../typedefs'

// Temporary solution to ensure we have a central registry over used helpIds
export const HELP_IDS = {
TYPE_INVALID: 'schema-type-invalid',
TYPE_IS_ESM_MODULE: 'schema-type-is-esm-module',
TYPE_NAME_RESERVED: 'schema-type-name-reserved',
TYPE_MISSING_NAME: 'schema-type-missing-name-or-type',
TYPE_MISSING_TYPE: 'schema-type-missing-name-or-type',
Expand Down
27 changes: 26 additions & 1 deletion packages/@sanity/schema/src/sanity/validation/types/rootType.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
import {error, HELP_IDS, warning} from '../createValidationResult'

export default (typeDef, visitorContext) => {
const hasName = Boolean(typeDef.name)
if (!hasName && Object.keys(typeDef).length === 1) {
// Short-circuit on obviously invalid types (only key is _problems)
return {
...typeDef,
_problems: [
error(
'Invalid/undefined type declaration, check declaration or the import/export of the schema type.',
HELP_IDS.TYPE_INVALID
)
]
}
}

const problems = []
if (!typeDef.name) {
if (looksLikeEsmModule(typeDef)) {
problems.push(
error(
'Type appears to be an ES6 module imported through CommonJS require - use an import statement or access the `.default` property',
HELP_IDS.TYPE_IS_ESM_MODULE
)
)
} else if (!hasName) {
problems.push(error('Missing type name', HELP_IDS.TYPE_MISSING_NAME))
} else if (visitorContext.isReserved(typeDef.name)) {
problems.push(
Expand Down Expand Up @@ -33,3 +54,7 @@ export default (typeDef, visitorContext) => {
_problems: problems
}
}

function looksLikeEsmModule(typeDef) {
return !typeDef.name && typeDef.default && (typeDef.default.name || typeDef.default.title)
}

0 comments on commit c6cd728

Please sign in to comment.