Skip to content

Commit

Permalink
fix: Optimized generateAggregateMacros logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jlacivita committed Apr 4, 2024
1 parent 0a45045 commit 649a17a
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 153 deletions.
3 changes: 3 additions & 0 deletions src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import sdk from './sdk/index.mjs'
import docs from './docs/index.mjs'
import openrpc from './openrpc/index.mjs'
import validate from './validate/index.mjs'
import update from './update/index.mjs'

import nopt from 'nopt'
import path from 'path'
Expand All @@ -13,6 +14,8 @@ import url from 'url'
const knownOpts = {
'input': [path],
'output': [path],
'client': [path],
'server': [path],
'sdk': [path],
'schemas': [path, Array],
'template': [path],
Expand Down
228 changes: 127 additions & 101 deletions src/macrofier/engine.mjs

Large diffs are not rendered by default.

54 changes: 22 additions & 32 deletions src/macrofier/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { emptyDir, readDir, readFiles, readFilesPermissions, readJson,
import { getTemplate, getTemplateForModule } from '../shared/template.mjs'
import { getModule, hasPublicAPIs } from '../shared/modules.mjs'
import { logHeader, logSuccess } from '../shared/io.mjs'
import Types from './types.mjs'
import path from 'path'
import engine from './engine.mjs'
import { getLocalSchemas, replaceRef, replaceUri } from '../shared/json-schema.mjs'
Expand All @@ -31,7 +32,8 @@ import { getLocalSchemas, replaceRef, replaceUri } from '../shared/json-schema.m
/******************************************** MAIN **********************************************/
/************************************************************************************************/
const macrofy = async (
input,
server,
client,
template,
output,
options
Expand Down Expand Up @@ -73,23 +75,11 @@ const macrofy = async (
} = options

return new Promise( async (resolve, reject) => {
const openrpc = await readJson(input)
const serverRpc = await readJson(server)
const clientRpc = await readJson(client)

logHeader(`Generating ${headline} for version ${openrpc.info.title} ${openrpc.info.version}`)
logHeader(`Generating ${headline} for version ${serverRpc.info.title} ${serverRpc.info.version}`)

let typer

try {
// const typerModule = await import(path.join(sharedTemplates, '..', 'Types.mjs'))
// typer = typerModule.default
}
catch (_) {
// typer = (await import('../shared/typescript.mjs')).default
}

typer = (await import('./types.mjs')).default

engine.setTyper(typer)
engine.setConfig({
copySchemasIntoModules,
mergeOnTitle,
Expand All @@ -106,16 +96,16 @@ const macrofy = async (
operators
})

const moduleList = [...(new Set(openrpc.methods.map(method => method.name.split('.').shift())))]
const moduleList = [...(new Set(serverRpc.methods.map(method => method.name.split('.').shift())))]
const sdkTemplateList = template ? await readDir(template, { recursive: true }) : []
const sharedTemplateList = await readDir(sharedTemplates, { recursive: true })
const templates = Object.assign(await readFiles(sharedTemplateList, sharedTemplates),
await readFiles(sdkTemplateList, template)) // sdkTemplates are second so they win ties

typer.setTemplates && typer.setTemplates(templates)
typer.setPrimitives(primitives)
typer.setAllocatedPrimitiveProxies(allocatedPrimitiveProxies)
typer.setConvertTuples(convertTuplesToArraysOrObjects)
Types.setTemplates && Types.setTemplates(templates)
Types.setPrimitives(primitives)
Types.setAllocatedPrimitiveProxies(allocatedPrimitiveProxies)
Types.setConvertTuples(convertTuplesToArraysOrObjects)

let templatesPermission = {}
if (persistPermission) {
Expand All @@ -140,20 +130,20 @@ const macrofy = async (

let modules
if (hidePrivate) {
modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules, extractSubSchemas)).filter(hasPublicAPIs)
modules = moduleList.map(name => getModule(name, serverRpc, copySchemasIntoModules, extractSubSchemas)).filter(hasPublicAPIs)
}
else {
modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules, extractSubSchemas))
modules = moduleList.map(name => getModule(name, serverRpc, copySchemasIntoModules, extractSubSchemas))
}

// Grab all schema groups w/ a URI string. These came from some external json-schema that was bundled into the OpenRPC
const externalSchemas = {}
openrpc.components && openrpc.components.schemas
&& Object.entries(openrpc.components.schemas).filter(([_, schema]) => schema.$id).forEach(([name, schema]) => {
serverRpc.components && serverRpc.components.schemas
&& Object.entries(serverRpc.components.schemas).filter(([_, schema]) => schema.$id).forEach(([name, schema]) => {
const id = schema.$id
externalSchemas[id] = JSON.parse(JSON.stringify(schema))
replaceUri(id, '', externalSchemas[id])
Object.values(openrpc.components.schemas).forEach(schema => {
Object.values(serverRpc.components.schemas).forEach(schema => {
if (schema.$id && schema.$id !== id) {
externalSchemas[id].definitions[schema.$id] = schema
}
Expand All @@ -162,7 +152,7 @@ const macrofy = async (

const aggregatedExternalSchemas = mergeOnTitle ? Object.values(externalSchemas).filter(s => !modules.find(m => m.info.title === s.title)) : Object.values(externalSchemas)

const aggregateMacros = engine.generateAggregateMacros(openrpc, modules.concat(staticModules).concat(copySchemasIntoModules ? [] : Object.values(aggregatedExternalSchemas)), templates, libraryName)
const aggregateMacros = engine.generateAggregateMacros(serverRpc, modules.concat(staticModules).concat(copySchemasIntoModules ? [] : Object.values(aggregatedExternalSchemas)), templates, libraryName)

const outputFiles = Object.fromEntries(Object.entries(await readFiles( staticCodeList, staticContent))
.map( ([n, v]) => [path.join(output, n), v]))
Expand Down Expand Up @@ -201,7 +191,7 @@ const macrofy = async (

// Pick the index and defaults templates for each module.
templatesPerModule.forEach(t => {
const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: t, type: 'methods'})
const macros = engine.generateMacros(module, clientRpc, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: t, type: 'methods'})
let content = getTemplateForModule(module.info.title, 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
Expand All @@ -216,7 +206,7 @@ const macrofy = async (
})

primaryOutput.forEach(output => {
const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: output})
const macros = engine.generateMacros(module, clientRpc, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: output})
macros.append = append
outputFiles[output] = engine.insertMacros(outputFiles[output], macros)
})
Expand All @@ -227,7 +217,7 @@ const macrofy = async (
outputFiles[output] = engine.clearMacros(outputFiles[output]);
})

if (treeshakePattern && treeshakeEntry) {
if (false && treeshakePattern && treeshakeEntry) {
const importedFiles = (code, base) => Array.from(new Set([...code.matchAll(treeshakePattern)].map(arr => arr[2]))).map(i => path.join(output, base, i))

const treeShake = (entry, base='', checked = []) => {
Expand Down Expand Up @@ -267,7 +257,7 @@ const macrofy = async (

if (templatesPerSchema || primaryOutput.length) {
templatesPerSchema && templatesPerSchema.forEach( t => {
const macros = engine.generateMacros(document, templates, exampleTemplates, {hideExcluded: hideExcluded, createPolymorphicMethods: createPolymorphicMethods, destination: t})
const macros = engine.generateMacros(document, null, templates, exampleTemplates, {hideExcluded: hideExcluded, createPolymorphicMethods: createPolymorphicMethods, destination: t})
let content = getTemplate('/schemas', t, templates)
content = engine.insertMacros(content, macros)

Expand All @@ -278,7 +268,7 @@ const macrofy = async (
})

primaryOutput && primaryOutput.forEach(output => {
const macros = engine.generateMacros(document, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: output})
const macros = engine.generateMacros(document, null, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: output})
macros.append = append
outputFiles[output] = engine.insertMacros(outputFiles[output], macros)
})
Expand Down
48 changes: 31 additions & 17 deletions src/openrpc/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,27 @@ import { logHeader, logSuccess } from "../shared/io.mjs"

const run = async ({
input: input,
output: output,
client: client,
server: server,
template: template,
schemas: schemas,
}) => {

let openrpc = await readJson(template)
let serverOpenRPC = await readJson(template)
let clientOpenRPC = await readJson(template)
const sharedSchemaList = schemas ? (await Promise.all(schemas.map(d => readDir(d, { recursive: true })))).flat() : []
const sharedSchemas = await readFiles(sharedSchemaList)

try {
const packageJson = await readJson(path.join(input, '..', 'package.json'))
openrpc.info.version = packageJson.version
serverOpenRPC.info.version = packageJson.version
clientOpenRPC.info.version = packageJson.version
}
catch (error) {
// fail silently
}

logHeader(`Generating compiled ${openrpc.info.title} OpenRPC document version ${openrpc.info.version}`)
logHeader(`Generating compiled ${serverOpenRPC.info.title} OpenRPC document version ${serverOpenRPC.info.version}`)

Object.entries(sharedSchemas).forEach(([path, schema]) => {
const json = JSON.parse(schema)
Expand All @@ -55,6 +58,11 @@ const run = async ({
const descriptionsList = input ? await readDir(path.join(input, 'descriptions'), { recursive: true }) : []
const markdown = await readFiles(descriptionsList, path.join(input, 'descriptions'))

const isNotifier = method => method.tags.find(t => t.name === 'notifier')
const isProvider = method => method.tags.find(t => t.name === 'capabilities')['x-provides']
const isClientAPI = method => isNotifier(method) || isProvider(method)
const isServerAPI = method => !isClientAPI(method)

Object.keys(modules).forEach(key => {
let json = JSON.parse(modules[key])

Expand All @@ -70,45 +78,51 @@ const run = async ({
// merge any info['x-'] extension values (maps & arrays only..)
Object.keys(json.info).filter(key => key.startsWith('x-')).forEach(extension => {
if (Array.isArray(json.info[extension])) {
openrpc.info[extension] = openrpc.info[extension] || []
openrpc.info[extension].push(...json.info[extension])
serverOpenRPC.info[extension] = serverOpenRPC.info[extension] || []
serverOpenRPC.info[extension].push(...json.info[extension])
}
else if (typeof json.info[extension] === 'object') {
openrpc.info[extension] = openrpc.info[extension] || {}
serverOpenRPC.info[extension] = serverOpenRPC.info[extension] || {}
Object.keys(json.info[extension]).forEach(k => {
openrpc.info[extension][k] = json.info[extension][k]
serverOpenRPC.info[extension][k] = json.info[extension][k]
})
}
})

if (json.info.description) {
openrpc.info['x-module-descriptions'] = openrpc.info['x-module-descriptions'] || {}
openrpc.info['x-module-descriptions'][json.info.title] = json.info.description
serverOpenRPC.info['x-module-descriptions'] = serverOpenRPC.info['x-module-descriptions'] || {}
serverOpenRPC.info['x-module-descriptions'][json.info.title] = json.info.description
}


// add methods from this module
openrpc.methods.push(...json.methods)
serverOpenRPC.methods.push(...json.methods.filter(isServerAPI))
clientOpenRPC.methods.push(...json.methods.filter(isClientAPI))

// add schemas from this module
json.components && Object.assign(openrpc.components.schemas, json.components.schemas)
json.components && Object.assign(serverOpenRPC.components.schemas, json.components.schemas)

// add externally referenced schemas that are in our shared schemas path
serverOpenRPC = addExternalSchemas(serverOpenRPC, sharedSchemas)

// add externally referenced schemas that are in our shared schemas path
openrpc = addExternalSchemas(openrpc, sharedSchemas)
clientOpenRPC = addExternalSchemas(clientOpenRPC, sharedSchemas)

modules[key] = JSON.stringify(json, null, '\t')

logSuccess(`Generated the ${json.info.title} module.`)
})

openrpc = fireboltizeMerged(openrpc)
serverOpenRPC = fireboltizeMerged(serverOpenRPC)

await writeJson(output, openrpc)
await writeJson(server, serverOpenRPC)
await writeJson(client, clientOpenRPC)

console.log()
logSuccess(`Wrote file ${path.relative('.', output)}`)
logSuccess(`Wrote file ${path.relative('.', server)}`)
logSuccess(`Wrote file ${path.relative('.', client)}`)

return Promise.resolve()
}

export default run
export default run
7 changes: 4 additions & 3 deletions src/sdk/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ import macrofy from '../macrofier/index.mjs'
/************************************************************************************************/
// destructure well-known cli args and alias to variables expected by script
const run = async ({
input: input,
server: server,
client: client,
template: template,
output: output,
language: language,
Expand All @@ -40,7 +41,7 @@ const run = async ({

try {
// Important file/directory locations
const packageJsonFile = path.join(path.dirname(input), '..', 'package.json')
const packageJsonFile = path.join(path.dirname(server), '..', 'package.json')
const packageJson = await readJson(packageJsonFile)
mainFilename = path.basename(packageJson.main)
declarationsFilename = path.basename(packageJson.types)
Expand All @@ -51,7 +52,7 @@ const run = async ({

const config = await readJson(path.join(language, 'language.config.json'))

return macrofy(input, template, output, {
return macrofy(server, client, template, output, {
headline: 'SDK code',
outputDirectory: 'sdk',
sharedTemplates: path.join(language, 'templates'),
Expand Down
18 changes: 18 additions & 0 deletions src/shared/markdown.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
/*
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

const getFilename = (json, asPath) => (json ? json.info ? json.info.title : (asPath ? json.title : json.title + 'Schema'): '')
const getDirectory = (json, asPath) => asPath ? json.info ? '' : 'schemas' : ''

Expand Down
24 changes: 24 additions & 0 deletions src/shared/methods.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2021 Comcast Cable Communications Management, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

const tag = (method, name) => method.tags.find(tag => tag.name === name)
export const capabilities = method => tag(method, 'capabilities')
export const provides = method => capabilities(method)['x-provides']
export const pusher = method => capabilities(method)['x-push']
export const notifier = method => method.tags.find(t => t.name === 'notifier')
export const event = method => tag(method, 'event')
2 changes: 2 additions & 0 deletions src/update/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ function update(json) {
return method
})

json.methods = json.methods.filter(m => !m.tags.find(t => t.name === 'polymorphic-push'))

// look for pass-through providers w/ same name as use method
json.methods.filter(m => json.methods.filter(x => x.name === m.name).length > 1)
.filter(m => m.tags.find(t => t.name === 'capabilities')['x-provides'])
Expand Down

0 comments on commit 649a17a

Please sign in to comment.