Created
August 16, 2023 06:39
-
-
Save joduplessis/6e159dd41464b9b30b8160b5a626b3de to your computer and use it in GitHub Desktop.
Fastify plugin that generates React hooks & service methods from a config object in the header.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const workspacesReact = () => { | |
const [error, setError] = useState(null) | |
const [loading, setLoading] = useState(null) | |
const params = useParams() | |
const { some, shit } = location | |
const [name, setName] = useState('') | |
const handleWorkspacesPost = async () => { | |
setLoading(true) | |
try { | |
const { workspaceId } = params | |
const { workspace } = await workspacesPost(workspaceId, { name, image }, { some, shit }) | |
setWorkspace(workspace) | |
setLoading(false) | |
} catch (e) { | |
setLoading(false) | |
setError(e) | |
} | |
} | |
return null | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const API_HOST = '' | |
const getAuthToken = () => null | |
const createQueryVariables = (obj) => '' | |
export const workspacesPost = async (workspaceId, { name, image }, { some, shit }) => { | |
try { | |
const token = getAuthToken() | |
const url = API_HOST + `/workspaces/${workspaceId}?` + createQueryVariables({ some, shit }) | |
const result: any = await fetch(url, { | |
body: JSON.stringify({ name, image }), | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': 'Bearer ' + token, | |
}, | |
}) | |
if (result.headers.get('content-type') == 'application/json') { | |
return await result.json() | |
} else { | |
return result | |
} | |
} catch (error) { | |
throw error | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export default async (fastify, opts) => { | |
fastify.post('/workspaces', | |
{ | |
web: { | |
group: 'workspaces', | |
name: 'post', | |
body: 'name, image', | |
query: 'some, shit', | |
state: name, | |
result: workspace, | |
}, | |
}, | |
async (request, reply) => { | |
try { | |
reply.code(200).send({ success: true )) | |
} catch (error) { | |
reply.code(500).send(error) | |
} | |
} | |
) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require('dotenv').config() | |
import fp from 'fastify-plugin' | |
import fs from 'fs' | |
const toSentenceCase = function (str: string) { | |
return str | |
.split(' ') | |
.map((s) => s.toLowerCase()) | |
.map((s) => s.charAt(0).toUpperCase() + s.slice(1)) | |
.join(' ') | |
} | |
const prettier = require("prettier") | |
const capitalizeFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1) | |
const generateWebPathFromRoutePath = (routePath: string) => { | |
const parts = routePath.slice(1).split('/') | |
const urlParts = [] | |
const argParts = [] | |
parts.map((part) => { | |
if (part.charAt(0) == ':') { | |
const cleanedPart = part.slice(1) | |
urlParts.push('${' + cleanedPart + '}') | |
argParts.push(cleanedPart) | |
} else { | |
urlParts.push(part) | |
} | |
}) | |
return { | |
url: urlParts.join('/'), | |
arg: argParts.join(', '), | |
} | |
} | |
if (process.env.NODE_ENV !== 'production') { | |
fs.writeFileSync( | |
'web-services.ts', | |
` | |
const API_HOST = '' | |
const getAuthToken = () => null | |
const createQueryVariables = (obj) => '' | |
` | |
) | |
fs.writeFileSync( | |
'web-react.ts', | |
` | |
const useEffect = (a, b) => null | |
const useState = (a) => [a, (e: any) => console.log(e)] | |
const props: any = { children: null } | |
const body: any = {} | |
const query: any = {} | |
const other: any = {} | |
const location: any = {} | |
const joinRoom = (a) => null | |
const useRoom = (a, b) => null | |
const useParams: any = () => {} | |
const useConnection: any = (a) => {} | |
` | |
) | |
} | |
let reactFunctions: any = {} | |
const reactImports = [] | |
const reactPath = 'web-react.ts' | |
const servicesPath = 'web-services.ts' | |
const prettyConfig = { | |
"trailingComma": "es5", | |
"tabWidth": 4, | |
"semi": false, | |
"singleQuote": true, | |
"printWidth": 120, | |
"quoteProps": "consistent" | |
} | |
module.exports = fp(async function (fastify, options) { | |
fastify.addHook('onRoute', (routeOptions: any) => { | |
if (routeOptions.routePath !== '' && routeOptions.routePath !== '/*' && routeOptions.method !== 'HEAD' && process.env.NODE_ENV !== 'production') { | |
const { preValidation, method, url, path, handler, onSend, routePath, prefix, logLevel, attachValidation, web } = routeOptions | |
if (!web) return | |
const { group, name, body, query, state, result } = web | |
const webRoutePath = generateWebPathFromRoutePath(routePath) | |
const args = (() => { | |
const parts = [] | |
if (webRoutePath.arg) parts.push(`${webRoutePath.arg}`) | |
if (body) parts.push(`{ ${body} }`) | |
if (query) parts.push(`{ ${query} }`) | |
return parts.join(', ') | |
})() | |
const urlPath = query | |
? `API_HOST + '/${webRoutePath.url}?' + createQueryVariables({ ${query} })` | |
: `API_HOST + '/${webRoutePath.url}'` | |
const functionName = `${group}${toSentenceCase(name).split(' ').join('')}` | |
const functionNameReact = `${functionName}React` | |
const functionNameRequest = `handle${capitalizeFirstLetter(functionName)}` | |
reactImports.push(functionName) | |
// ------------------------------------------- | |
const servicesDeclaration = ` | |
export const ${functionName} = async (${args}) => { | |
try { | |
const token = getAuthToken() | |
const url = ${urlPath.split("'").join('`')} | |
const result: any = await fetch(url, { | |
body: ${body ? 'JSON.stringify({ ' + body + ' })' : 'null'}, | |
method: '${method}', | |
headers: { | |
'Content-Type': 'application/json', | |
'Authorization': 'Bearer ' + token | |
}, | |
}) | |
if (result.headers.get('content-type') == 'application/json') { | |
return await result.json() | |
} else { | |
return result | |
} | |
} catch (error) { | |
throw error | |
} | |
} | |
` | |
fs.appendFileSync(servicesPath, servicesDeclaration) | |
// ------------------------------------------- | |
if (!reactFunctions[group]) reactFunctions[group] = { state: [], functions: [], query: [] } | |
const functionDeclaration = ` | |
const ${functionNameRequest} = async () => { | |
setLoading(true) | |
try { | |
${webRoutePath.arg ? `const { ${webRoutePath.arg} } = params` : ``} | |
const ${result ? `{ ${result} }` : 'result'} = await ${functionName}(${args}) | |
${result ? result | |
.split(',') | |
.map(r => r.trim()) | |
.map((r) => `set${capitalizeFirstLetter(r)}(${r})`) | |
.join('\n') : ''} | |
setLoading(false) | |
} catch (e) { | |
setLoading(false) | |
setError(e) | |
} | |
} | |
` | |
reactFunctions[group].state = [...reactFunctions[group].state, ...(state || '').split(',').map(s => s.trim()).filter(s => !!s) ] | |
reactFunctions[group].query = [...reactFunctions[group].query, ...(query || '').split(',').map(q => q.trim()).filter(q => !!q) ] | |
reactFunctions[group].functions = [...reactFunctions[group].functions, functionDeclaration ] | |
} | |
}) | |
fastify.addHook('onReady', function (done) { | |
const reactGroups = Object.keys(reactFunctions).map(groupName => { | |
const { query, state, functions } = reactFunctions[groupName] | |
return ` | |
const ${groupName}React = () => { | |
const [error, setError] = useState(null) | |
const [loading, setLoading] = useState(null) | |
const params = useParams() | |
${query ? `const { ${Array.from(new Set(query)).join(', ')} } = location` : ``} | |
${state ? Array.from(new Set(state)) | |
.map((s) => `const [${s}, set${capitalizeFirstLetter(s)}] = useState('')`) | |
.join('\n') : ''} | |
${functions.map(f => f).join('\n')} | |
useConnection((state) => { | |
switch (state) { | |
case 'offline': return | |
case 'online': return | |
case 'restored': return // get() up to date data | |
} | |
}) | |
useRoom('${groupName}Id', ({ data, action }) => { | |
// this does a joinRoom('${groupName}Id') too | |
// action = UPDATE/DELETE/ETC | |
// const { name, image } = data | |
// setName(name) | |
// setImage(image) | |
}) | |
return null | |
} | |
` | |
}).join('\n') | |
fs.appendFileSync(reactPath, `\nimport { ${reactImports.join(', ')} } from './web-services' \n`) | |
fs.appendFileSync(reactPath, reactGroups) | |
const reactText = fs.readFileSync(reactPath, 'utf-8') | |
const formattedReactText = prettier.format(reactText, prettyConfig) | |
fs.writeFileSync(reactPath, formattedReactText) | |
const servicesText = fs.readFileSync(servicesPath, 'utf-8') | |
const formattedServicesText = prettier.format(servicesText, prettyConfig) | |
fs.writeFileSync(servicesPath, formattedServicesText) | |
const err = null | |
done(err) | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment