diff --git a/languages/c/src/types/NativeHelpers.mjs b/languages/c/src/types/NativeHelpers.mjs index 061f30b8..57aab2dd 100644 --- a/languages/c/src/types/NativeHelpers.mjs +++ b/languages/c/src/types/NativeHelpers.mjs @@ -60,7 +60,7 @@ const getNativeType = (json, fireboltString = false) => { } else if (jsonType === 'null' ) { type = 'void' - } + } return type } @@ -125,18 +125,13 @@ const getArrayAccessors = (arrayName, propertyType, valueType) => { return res } -const enumValue = (val,prefix) => { - const keyName = getSafeEnumKeyName(val) - return ` ${prefix.toUpperCase()}_${keyName.toUpperCase()}` -} - const generateEnum = (schema, prefix)=> { if (!schema.enum) { return '' } else { let str = `typedef enum {\n` - str += schema.enum.map(e => enumValue(e, prefix)).join(',\n') + str += schema.enum.map(e => getSafeEnumKeyName(val, prefix)).join(',\n') str += `\n} ${prefix};\n` return str } diff --git a/src/macrofier/engine.mjs b/src/macrofier/engine.mjs index 0696e5ff..347041ad 100644 --- a/src/macrofier/engine.mjs +++ b/src/macrofier/engine.mjs @@ -424,7 +424,7 @@ const promoteAndNameSubSchemas = (obj) => { } }) if (isSubSchema(method.result.schema)) { - addContentDescriptorSubSchema(method.result, '', obj) + addContentDescriptorSubSchema(method.result, '', obj) } else if (isEventMethod(method) && isSubSchema(getPayloadFromEvent(method))) { // TODO: the `1` below is brittle... should find the index of the non-ListenResponse schema @@ -1319,7 +1319,7 @@ function insertMethodMacros(template, methodObj, json, templates, type = '', exa const pullsForParamType = pullsParams ? types.getSchemaType(pullsParams, json, { destination: state.destination, section: state.section }) : '' const pullsForJsonType = pullsResult ? types.getSchemaType(pullsResult, json, { templateDir: 'json-types' }) : '' const pullsForParamJsonType = pullsParams ? types.getSchemaType(pullsParams, json, { templateDir: 'json-types' }) : '' - + const pullsEventParamName = event ? types.getSchemaInstantiation(event.result, json, event.name, { instantiationType: 'pull.param.name' }) : '' let seeAlso = '' @@ -1603,7 +1603,7 @@ function generateResultParams(result, json, templates, { name = '' } = {}) { return Object.entries(result.properties).map( ([name, type]) => template .replace(/\$\{method\.param\.name\}/g, name) .replace(/\$\{method\.param\.type\}/g, types.getSchemaType(type, json, { moduleTitle: moduleTitle, result: true, namespace: true})) - ).join(', ') // most languages separate params w/ a comma, so leaving this here for now + ).join(', ') // most languages separate params w/ a comma, so leaving this here for now } // tuples get unwrapped else if (config.unwrapResultObjects && result.type && result.type === 'array' && Array.isArray(result.items)) { diff --git a/src/macrofier/index.mjs b/src/macrofier/index.mjs index 99a1a3e0..c76be0f4 100644 --- a/src/macrofier/index.mjs +++ b/src/macrofier/index.mjs @@ -148,7 +148,7 @@ const macrofy = async ( const outputFiles = Object.fromEntries(Object.entries(await readFiles( staticCodeList, staticContent)) .map( ([n, v]) => [path.join(output, n), v])) - + let primaryOutput = [] Object.keys(templates).forEach(file => { @@ -220,7 +220,7 @@ const macrofy = async ( if (!checked.includes(entry)) { imports = importedFiles(code, base) - checked.push(entry) + checked.push(entry) } imports = imports.map(imp => Array.from(new Set([imp, ...treeShake(imp, path.dirname(imp).substring(output.length), checked)]))).flat() @@ -244,7 +244,7 @@ const macrofy = async ( // Grab all schema groups w/ a URI string. These came from some external json-schema that was bundled into the OpenRPC const externalSchemas = {} openrpc['x-schemas'] - && Object.entries(openrpc['x-schemas']).forEach(([name, schema]) => { + && Object.entries(openrpc['x-schemas']).forEach(([name, schema]) => { if (schema.uri) { const id = schema.uri externalSchemas[id] = externalSchemas[id] || { $id: id, info: {title: name }, methods: []} @@ -278,19 +278,19 @@ const macrofy = async ( } }) }) - + // Output any schema templates for each bundled external schema document Object.values(externalSchemas).forEach( document => { if (templatesPerSchema) { templatesPerSchema.forEach( t => { const macros = engine.generateMacros(document, templates, exampleTemplates, {hideExcluded: hideExcluded, createPolymorphicMethods: createPolymorphicMethods, destination: t}) let content = getTemplate('/schemas', t, templates) - + // NOTE: whichever insert is called first also needs to be called again last, so each phase can insert recursive macros from the other content = engine.insertMacros(content, macros) - + const location = createModuleDirectories ? path.join(output, document.info.title, t) : path.join(output, t.replace(/module/, document.info.title.toLowerCase()).replace(/index/, document.info.title)) - + outputFiles[location] = content logSuccess(`Generated macros for schema ${path.relative(output, location)}`) }) diff --git a/src/macrofier/types.mjs b/src/macrofier/types.mjs index 55ef7b7e..d036cd97 100644 --- a/src/macrofier/types.mjs +++ b/src/macrofier/types.mjs @@ -226,8 +226,8 @@ const insertEnumMacros = (content, schema, module, name, suffix, templateDir = " schema.enum.map(value => { if (!value) { value = getTemplate(path.join(templateDir, 'unset' + suffix)) - } - value ? values.push(template[i].replace(/\$\{key\}/g, getSafeEnumKeyName(value)) + } + value ? values.push(template[i].replace(/\$\{key\}/g, getSafeEnumKeyName(value, schema.enumKeyPrefix)) .replace(/\$\{value\}/g, value)) : '' }) template[i] = values.map((value, id) => { @@ -543,7 +543,7 @@ function getSchemaShape(schema = {}, module = {}, { templateDir = 'types', paren const suffix = destination && ('.' + destination.split('.').pop()) || '' const theTitle = insertSchemaMacros(getTemplate(path.join(templateDir, 'title' + suffix)), schema, module, { name: schema.title, parent, property, required, recursive: false }) - let result = getTemplate(path.join(templateDir, 'default' + suffix)) || '${shape}' + let result = getTemplate(path.join(templateDir, 'default' + suffix)) || '${shape}' let genericTemplate = getTemplate(path.join(templateDir, 'generic' + suffix)) if (enums && level === 0 && Array.isArray(schema.enum) && ((schema.type === "string") || (schema.type[0] === "string"))) { @@ -852,7 +852,7 @@ function getSchemaType(schema, module, { destination, templateDir = 'types', lin const shape = insertAnyOfMacros(getTemplate(path.join(templateDir, 'anyOf' + suffix)), schema, module, theTitle) return insertSchemaMacros(shape, schema, module, { name: theTitle, recursive: false }) - + // if (event) { // return getSchemaType((schema.oneOf || schema.anyOf)[0], module, { destination, link, title, code, asPath, baseUrl }) // } diff --git a/src/shared/json-schema.mjs b/src/shared/json-schema.mjs index 5d1d3a27..7af03e3e 100644 --- a/src/shared/json-schema.mjs +++ b/src/shared/json-schema.mjs @@ -42,7 +42,7 @@ const objectPaths = obj => { return isObject(value) ? product.concat(paths(value, fullPath)) : product.concat(fullPath) - }, []) : [] + }, []) : [] } return paths(obj); } @@ -100,7 +100,7 @@ const replaceUri = (existing, replacement, schema) => { Object.keys(schema).forEach(key => { replaceUri(existing, replacement, schema[key]) }) - } + } } const replaceRef = (existing, replacement, schema) => { @@ -176,7 +176,7 @@ function getSchemaConstraints(schema, module, options = { delimiter: '\n' }) { typeof schema.exclusiveMinimum === 'number' ? constraints.push(`exclusiveMinimum: ${schema.exclusiveMinimum}`) : null typeof schema.multipleOf === 'number' ? constraints.push(`multipleOf: ${schema.multipleOf}`) : null - return constraints.join(options.delimiter) + return constraints.join(options.delimiter) } else if (schema.type === 'array' && schema.items) { let constraints = [] @@ -188,7 +188,7 @@ function getSchemaConstraints(schema, module, options = { delimiter: '\n' }) { constraints = [getSchemaConstraints(schema.items, module, options)] } - return constraints.join(options.delimiter) + return constraints.join(options.delimiter) } else if (schema.oneOf || schema.anyOf) { return '' //See OpenRPC Schema for `oneOf` and `anyOf` details' @@ -228,7 +228,7 @@ const localizeDependencies = (json, document, schemas = {}, options = defaultLoc if (!options.externalOnly) { while (refs.length > 0) { for (let i=0; i 1) { @@ -241,7 +241,7 @@ const localizeDependencies = (json, document, schemas = {}, options = defaultLoc resolvedSchema = { "$REF": ref} unresolvedRefs.push([...path]) } - + if (path.length) { // don't loose examples from original object w/ $ref // todo: should we preserve other things, like title? @@ -252,22 +252,22 @@ const localizeDependencies = (json, document, schemas = {}, options = defaultLoc else { delete definition['$ref'] Object.assign(definition, resolvedSchema) - } + } } } refs = getLocalSchemaPaths(definition) } } - + refs = getExternalSchemaPaths(definition) while (refs.length > 0) { for (let i=0; i value.split(':').pop() // use last portion of urn:style:values - .replace(/[\.\-]/g, '_') // replace dots and dashes - .replace(/\+/g, '_plus') // change + to _plus - .replace(/([a-z])([A-Z0-9])/g, '$1_$2') // camel -> snake case - .replace(/^([0-9]+(\.[0-9]+)?)/, 'v$1') // insert `v` in front of things that look like version numbers - .toUpperCase() +const getSafeEnumKeyName = function(value, keyPrefix = '') { + if (keyPrefix != '') { + value = keyPrefix + '_' + value + } + + let key = value.split(':').pop() // use last portion of urn:style:values + .replace(/\+/g, '_plus') // change + to _plus + .replace(/[\.\-\/\;]/g, '_') // replace special characters + .replace(/([a-z])([A-Z0-9])/g, '$1_$2') // camel -> snake case + .replace(/^([0-9]+\_([0-9]+)?)/, 'v$1') // insert `v` in front of things that look like version numbers + + return key.toUpperCase() +} export { getSchemaConstraints, @@ -450,4 +457,4 @@ export { removeIgnoredAdditionalItems, mergeAnyOf, mergeOneOf -} +} diff --git a/src/shared/typescript.mjs b/src/shared/typescript.mjs index 930549f1..4689f0c3 100644 --- a/src/shared/typescript.mjs +++ b/src/shared/typescript.mjs @@ -26,7 +26,7 @@ function getMethodSignature(method, module, { destination, isInterface = false } typescript += getMethodSignatureParams(method, module, { destination }) typescript += '): ' + (isSynchronous(method) ? getSchemaType(method.result.schema, module, {title: true}) : 'Promise<' + getSchemaType(method.result.schema, module, {title: true}) + '>') - + return typescript } @@ -80,9 +80,9 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, } else if (schema.type === 'object') { let suffix = '{' - + structure.push(' '.repeat(level) + `${prefix}${theTitle}${operator} ${suffix}`) - + if (schema.properties) { Object.entries(schema.properties).forEach(([name, prop]) => { if (!schema.required || !schema.required.includes(name)) { @@ -100,7 +100,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, if (schema.additionalProperties && (typeof schema.additionalProperties === 'object')) { type = getSchemaType(schema.additionalProperties, module) - } + } if (schema.patternProperties) { Object.entries(schema.patternProperties).forEach(([pattern, schema]) => { @@ -110,7 +110,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, } }) } - + structure.push(getSchemaShape({type: type}, module, {name: safeName(prop), descriptions: descriptions, level: level+1})) }) } @@ -119,7 +119,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, let type = getSchemaType(schema.additionalProperties, module, { destination }) structure.push(getSchemaShape({type: type}, module, {name: '[property: string]', descriptions: descriptions, level: level+1})) } - + structure.push(' '.repeat(level) + '}') } else if (schema.anyOf) { @@ -155,11 +155,11 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, else if (schema.type || schema.const) { const isArrayWithSchemaForItems = schema.type === 'array' && schema.items && !Array.isArray(schema.items) const isArrayWithSpecificItems = schema.type === 'array' && schema.items && Array.isArray(schema.items) - + // TODO: deal with fixed sized arrays vs arbitrary arrays let suffix let summary = '' - + if (schema.const) { suffix = JSON.stringify(schema.const) } @@ -172,16 +172,16 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, else { suffix = getSchemaType(schema, module, { title: level ? true : false }) // prefer schema title over robust descriptor } - + // if there's a summary or description, append it as a comment (description only gets first line) if (level > 0 && (summary || schema.description)) { summary = `\t// ${summary || schema.description.split('\n')[0]}` } - + if (suffix === 'array') { suffix = '[]' } - + if (theTitle === suffix) { return ' '.repeat(level) + `${prefix}${theTitle}` } @@ -189,9 +189,9 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, return ' '.repeat(level) + `${prefix}${theTitle}${operator} ${suffix}${summary}` } } - + structure = structure.join('\n').split('\n') - + if (level === 0) { const length = str => str.length let max = Math.max(...structure.map(l => l.split('\t//')[0]).map(length)) + 2 @@ -202,7 +202,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, function getSchemaType(schema, module, { destination, link = false, title = false, code = false, asPath = false, event = false, expandEnums = true, baseUrl = '' } = {}) { const wrap = (str, wrapper) => wrapper + str + wrapper - + if (schema['$ref']) { if (schema['$ref'][0] === '#') { return getSchemaType(getPath(schema['$ref'], module), module, {title: true, link: link, code: code, destination}) @@ -259,11 +259,11 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, else if (schema.type === 'array' && schema.items) { if (Array.isArray(schema.items)) { let type = '[' + schema.items.map(x => getSchemaType(x, module, { destination })).join(', ') + ']' // no links, no code - + if (code) { type = wrap(type, '`') } - + return type } else { @@ -358,4 +358,4 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, getSchemaType, getJsonType, getSchemaInstantiation - } \ No newline at end of file + } diff --git a/src/validate/index.mjs b/src/validate/index.mjs index f9002554..0b4d426a 100644 --- a/src/validate/index.mjs +++ b/src/validate/index.mjs @@ -108,7 +108,7 @@ const run = async ({ addFormats(ajv) // explicitly add our custom extensions so we can keep strict mode on (TODO: put these in a JSON config?) - ajv.addVocabulary(['x-method', 'x-this-param', 'x-additional-params', 'x-schemas', 'components', 'x-property']) + ajv.addVocabulary(['x-method', 'x-this-param', 'x-additional-params', 'x-schemas', 'components', 'x-property', 'enumKeyPrefix']) const firebolt = ajv.compile(fireboltOpenRpcSpec) const jsonschema = ajv.compile(jsonSchemaSpec) @@ -151,7 +151,7 @@ const run = async ({ } }) - const examples = ajv.compile(exampleSpec) + const examples = ajv.compile(exampleSpec) let result = validate(json, {}, ajv, jsonschema) let exampleResult = validate(json, {}, ajv, examples) @@ -301,4 +301,4 @@ const run = async ({ return Promise.resolve() } -export default run \ No newline at end of file +export default run