in compiler/src/model/utils.ts [781:900]
function hoistPropertyAnnotations (property: model.Property, jsDocs: JSDoc[]): void {
// in most of the cases the jsDocs comes in a single block,
// but it can happen that the user defines multiple single line jsDoc.
// We want to enforce a single jsDoc block.
assert(jsDocs, jsDocs.length < 2, 'Use a single multiline jsDoc block instead of multiple single line blocks')
const validTags = ['prop_serializer', 'doc_url', 'aliases', 'codegen_name', 'server_default',
'variant', 'doc_id', 'es_quirk', 'availability', 'ext_doc_id']
const tags = parseJsDocTags(jsDocs)
if (jsDocs.length === 1) {
const description = jsDocs[0].getDescription()
if (description.length > 0) property.description = description.trim().replace(/\r/g, '')
}
if (tags.doc_id != null) {
assert(
jsDocs,
tags.doc_url == null,
'You can\'t define @doc_id and @doc_url at the same time'
)
}
setTags(jsDocs, property, tags, validTags, (tags, tag, value) => {
if (tag.endsWith('_serializer')) {
} else if (tag === 'aliases') {
property.aliases = parseCommaSeparated(value)
} else if (tag === 'codegen_name') {
property.codegenName = value
} else if (tag === 'doc_url') {
assert(jsDocs, isValidUrl(value), '@doc_url is not a valid url')
property.docUrl = value.replace(/\r/g, '')
} else if (tag === 'availability') {
// The @availability jsTag is different than most because it allows
// multiple values within the same docstring, hence needing to parse
// the values again in order to preserve multiple values.
const jsDocsMulti = parseJsDocTagsAllowDuplicates(jsDocs)
const availabilities = parseAvailabilityTags(jsDocs, jsDocsMulti.availability)
// The absence of an 'availability' field on a property implies that
// the property is available in all flavors.
property.availability = {}
for (const [availabilityName, availabilityValue] of Object.entries(availabilities)) {
property.availability[availabilityName] = availabilityValue
}
} else if (tag === 'doc_id') {
assert(jsDocs, value.trim() !== '', `Property ${property.name}'s @doc_id is cannot be empty`)
property.docId = value
const docUrl = docIds.find(entry => entry[0] === value)
if (docUrl != null) {
property.docUrl = docUrl[1].replace(/\r/g, '')
}
} else if (tag === 'ext_doc_id') {
assert(jsDocs, value.trim() !== '', `Property ${property.name}'s @ext_doc_id is cannot be empty`)
property.extDocId = value
const docUrl = docIds.find(entry => entry[0] === value)
if (docUrl != null) {
property.extDocUrl = docUrl[1].replace(/\r/g, '')
}
} else if (tag === 'server_default') {
assert(jsDocs, property.type.kind === 'instance_of' || property.type.kind === 'union_of' || property.type.kind === 'array_of', `Default values can only be configured for instance_of or union_of types, you are using ${property.type.kind}`)
assert(jsDocs, !property.required, 'Default values can only be specified on optional properties')
if (property.type.kind === 'union_of') {
let valueType = ''
if (value === 'true' || value === 'false') {
valueType = 'boolean'
} else if (!isNaN(Number(value))) {
valueType = 'number'
} else {
valueType = 'string'
}
const unionTypes = property.type.items.map(item => {
assert(jsDocs, item.kind === 'instance_of', `Default values in unions can only be configured for instance_of types, you are using ${property.type.kind}`)
if (['short', 'byte', 'integer', 'uint', 'long', 'ulong', 'float', 'double', 'Percentage'].includes(item.type.name)) {
return 'number'
}
return item.type.name
})
assert(jsDocs, unionTypes.includes(valueType), `The configured server_default value is not present in the union value: ${unionTypes.join(' | ')}`)
property.serverDefault = value
} else if (property.type.kind === 'array_of') {
try {
value = eval(value) // eslint-disable-line
} catch (err) {
assert(jsDocs, false, 'The default value is not formatted properly')
}
assert(jsDocs, Array.isArray(value), 'The default value should be an array')
property.serverDefault = value
} else {
switch (property.type.type.name) {
case 'boolean':
assert(jsDocs, value === 'true' || value === 'false', `The default value for ${property.name} should be a boolean`)
property.serverDefault = value === 'true'
break
case 'number':
case 'short':
case 'byte':
case 'integer':
case 'uint':
case 'long':
case 'ulong':
case 'float':
case 'double':
case 'Percentage':
assert(jsDocs, !isNaN(Number(value)), `The default value for ${property.name} should be a number`)
property.serverDefault = Number(value)
break
default:
property.serverDefault = value
}
}
} else if (tag === 'variant') {
assert(jsDocs, value === 'container_property', `Unknown 'variant' value '${value}' on property ${property.name}`)
property.containerProperty = true
} else if (tag === 'es_quirk') {
property.esQuirk = value
} else {
assert(jsDocs, false, `Unhandled tag: '${tag}' with value: '${value}' on property ${property.name}`)
}
})
}